AIDL背景

在Android平台上,一個進程通常無法訪問另一個進程的內存,所以想要跨進程訪問的話,需要將要傳遞的數據分解為系統可以支持和識別的基本單元,有序的經過進程的邊界。因為這個操作十分繁瑣,所以Android使用AIDL來解決這個問題。AIDL就是用於生成兩個進程間進行進程間通信的(IPC)代碼,面向開發簡化這個過程。

注:只有允許不同應用的客户端用 IPC 方式訪問服務,並且想要在服務中處理多線程時,才有必要使用 AIDL。 如果您不需要執行跨越不同應用的併發 IPC,就應該通過實現一個 Binder 創建接口;或者,如果您想執行 IPC,但根本不需要處理多線程,則使用 Messenger 類來實現接口。無論如何,在實現 AIDL 之前,請您務必理解綁定服務。

定義AIDL接口

  • 創建一個.aidl文件。
  • 實現接口,此接口具有一個名為 stub 的內部抽象類,用於擴展 Binder 類並實現 aild 定義的接口。
  • 向客户端公開接口,實現 Service 並重寫 onBind() 返回給 Stub 類的實現。

創建aidl文件

默認情況下,aidl支持的數據類型有:

  • 基本數據類型( int, long, char, boolean, double等等)
  • String 和 CharSequence
  • List : 每個元素都必須被 aidl 支持,只支持ArrayList
  • Map : 每個元素都必須被 aidl 支持,支持支HashMap
  • Parcelable : 所有實現了 Parcelable 接口的對象
  • aidl : 所有 aidl 接口本身也可以在 aidl 文件中使用

一個 aidl 文件例子 :

// IStudentManager.aidl
package com.seaicelin.aidl;

import com.seaicelin.aidl.Student;

interface IStudentManager {
    List<Student> getStudentList();
    void addStudent(in Student student);
}

Android Studio會自動生成一個 aidl 文件相對應的 java 接口文件,它包含一個名為 stub 的抽象類和他的代理類 proxy。

package com.seaicelin.aidl;

public interface IStudentManager extends android.os.IInterface {

    //Local-side IPC implementation stub class.
    public static abstract class Stub extends android.os.Binder implements com.seaicelin.aidl.IStudentManager {
        private static final java.lang.String DESCRIPTOR = "com.seaicelin.aidl.IStudentManager";

        //Construct the stub at attach it to the interface.
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        //Cast an IBinder object into an com.seaicelin.aidl.IStudentManager   
        //interface,generating a proxy if needed.
        public static com.seaicelin.aidl.IStudentManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.seaicelin.aidl.IStudentManager))) {
                return ((com.seaicelin.aidl.IStudentManager) iin);
            }
            return new com.seaicelin.aidl.IStudentManager.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getStudentList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.seaicelin.aidl.Student> _result = this.getStudentList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addStudent: {
                    data.enforceInterface(DESCRIPTOR);
                    com.seaicelin.aidl.Student _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.seaicelin.aidl.Student.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addStudent(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.seaicelin.aidl.IStudentManager {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public java.util.List<com.seaicelin.aidl.Student> getStudentList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.seaicelin.aidl.Student> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getStudentList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.seaicelin.aidl.Student.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addStudent(com.seaicelin.aidl.Student student) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((student != null)) {
                        _data.writeInt(1);
                        student.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addStudent, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_getStudentList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public java.util.List<com.seaicelin.aidl.Student> getStudentList() throws android.os.RemoteException;

    public void addStudent(com.seaicelin.aidl.Student student) throws android.os.RemoteException;
}

遠程服務端service實現接口

服務端實現上文中提到的 stub 抽象類,實現 aidl 接口。這裏的 mBinder 是 Stub 的一個 實例,用於定義服務的 RPC 接口;在下一步中,將向客户端公開該實例,以便客户端能與服務端進行交互。

<service
    android:name=".StudentManagerService"
    android:enabled="true"
    android:exported="true"
    android:process=":remote">
</service>

public class StudentManagerService extends Service {

    private static final String TAG = "[StudentManagerService]";

    private CopyOnWriteArrayList<Student> mStudentList = new CopyOnWriteArrayList<>();

    public StudentManagerService() {
    }

    @Override
    public void onCreate(){
        super.onCreate();
        mStudentList.add(new Student(1, "seaicelin1"));
        mStudentList.add(new Student(2, "seaicelin2"));
        mStudentList.add(new Student(3, "seaicelin3"));
    }

    //實現aidl生成的java文件的Stub抽象類並實現aidl方法
    //aidl方法在Binder線程池中執行,多個客户端同時連接時,需要處理線程同步
    //CopyOnWriteArrayList支持併發讀寫,支持線程同步
    private Binder mBinder = new IStudentManager.Stub(){

        @Override
        public List<Student> getStudentList() throws RemoteException {
            return mStudentList;
        }

        @Override
        public void addStudent(Student student) throws RemoteException {
            if(mStudentList.contains(student))
                return;
            mStudentList.add(student);
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

}

客户端實現

在 service 內部實現接口後,需要向客户端公開該接口,以便客户端綁定。服務端需要創建一個 service 並實現 onBind() ,返回一個實例,這個實例實現了上文提到的 Stub 抽象類。

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private static final String TAG = "[MainActivity]";
    private Button mBtnService;
    private IStudentManager mStudentManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBtnService = (Button) findViewById(R.id.button);
        mBtnService.setOnClickListener(this);

    }
    //創建 ServiceConnection 對象
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {

            mStudentManager = IStudentManager.Stub.asInterface(service);
            try {
                List<Student> list = mStudentManager.getStudentList();

                for(int i = 0; i < list.size(); i++){
                    Log.e(TAG, "query student list " + list.get(i).toString());
                }

                Student student = new Student(4, "seaicelin4");
                mStudentManager.addStudent(student);

                List<Student> newList = mStudentManager.getStudentList();

                for(int i = 0; i < newList.size(); i++){
                    Log.e(TAG, "after add student, query student list " + newList.get(i).toString());
                }

            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };


    @Override
    public void onClick(View v) {
        Intent intent = new Intent(this, StudentManagerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
        startService(intent);
        Log.e(TAG, "onClick");
    }

    @Override
    protected void onDestroy(){
        unbindService(mConnection);
        super.onDestroy();
    }
}

由此,客户端創建完畢,查詢服務端的有多少個學生,然後增加一個學生,最後再次打印服務端的學生數。我們可以通過 log 來看看是否已經能夠正常使用。

03-25 09:17:09.117 31597-31597/com.seaicelin.aidl E/[MainActivity]: onClick
03-25 09:17:09.129 31597-31597/com.seaicelin.aidl E/[MainActivity]: query student list id = 1 name = seaicelin1
03-25 09:17:09.129 31597-31597/com.seaicelin.aidl E/[MainActivity]: query student list id = 2 name = seaicelin2
03-25 09:17:09.129 31597-31597/com.seaicelin.aidl E/[MainActivity]: query student list id = 3 name = seaicelin3
03-25 09:17:09.133 31597-31597/com.seaicelin.aidl E/[MainActivity]: after add student, query student list id = 1 name = seaicelin1
03-25 09:17:09.133 31597-31597/com.seaicelin.aidl E/[MainActivity]: after add student, query student list id = 2 name = seaicelin2
03-25 09:17:09.133 31597-31597/com.seaicelin.aidl E/[MainActivity]: after add student, query student list id = 3 name = seaicelin3
03-25 09:17:09.133 31597-31597/com.seaicelin.aidl E/[MainActivity]: after add student, query student list id = 4 name = seaicelin4

到這裏,已經成功使用 aidl 進行跨進程通信,aidl 的基本使用介紹完畢。


總結調用 IPC 方法

用谷歌官方文檔作為總結吧,調用類必須執行以下步驟,才能調用使用 AIDL 定義的遠程接口:

  • 在項目 src/ 目錄中加入 .aidl 文件。
  • 聲明一個 IBinder 接口實例(基於 AIDL 生成)。
  • 實現 ServiceConnection。
  • 調用 Context.bindService(),以傳入您的 ServiceConnection 實現。
  • 在您的 onServiceConnected() 實現中,您將收到一個 IBinder 實例(名為 service)。調用 YourInterfaceName.Stub.asInterface((IBinder)service),以將返回的參數轉換為 YourInterface 類型。
  • 調用您在接口上定義的方法。您應該始終捕獲 DeadObjectException 異常,它們是在連接中斷時引發的;這將是遠程方法引發的唯一異常。
  • 如需斷開連接,請使用您的接口實例調用 Context.unbindService()。