Android進程間通信的方式之AIDL

一、AIDL是什麼

它全稱是Android Interface Definition Language,即Android接口定義語言,爲了使其他的進程也可以訪問本進程提供的服務,Android使用AIDL來公開服務的接口,它裏面定義了本進程可以爲其他進程提供什麼服務,即定義了一些方法,其他進程就可以通過RPC(遠程調用)來調用這些方法,從而獲得服務,其中提供服務的進程稱爲服務端,獲取服務的進程稱爲客戶端。

二、AIDL接口的創建

AIDL接口用來暴露服務點提供給客戶端的方法,新建一個AIDL接口文件,只需要在你的項目中 點擊包名 -> 右鍵 -> new -> AIDL -> Aidl.file,然後輸入AIDL接口名稱,這裏我輸入了IUserManager,然後點擊Finish,就會在你的main目錄下創建了一個aidl文件夾,aidl文件夾裏的包名和java文件夾裏的包名相同,裏面用來存放AIDL接口文件,如下:

在裏面你會發現你剛剛創建的AIDL接口IUserManager,點進去,如下:

// IUserManager.aidl
package com.example.aidltest;

// Declare any non-default types here with import statements

interface IUserManager {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

裏面聲明瞭一個方法,寫着AIDL支持的一些數據類型(int、long、boolean、float、double、String),除了這些,AIDL還支持其他的基本數據類型、ArrayList(裏面的每個元素都要被AIDL支持)、HashMap(裏面的每個元素都要被AIDL支持)、實現了Parcelable接口的對象和AIDL接口本身,還有AIDL接口中只支持聲明方法,不支持聲明靜態常量。

其中如果要在AIDL接口文件中使用AIDL對象,必須顯式的 import 進來,即使它們在同一個包內,還有如果在AIDL接口文件用到了Parcelable對象,必須新建一個和它同名的AIDL文件,並在其中聲明它爲parcelable類型,接下來我要使用User這個Parcelable對象,所以我要在aidl文件夾下新建一個和他同名的AIDL文件,如下:

然後在User.aidl中添加如下內容:

// User.aidl
package com.example.aidltest;

parcelable User;

在裏面,我聲明瞭User.java這個對象爲parcelable類型,接下來把IUserManager中的basicTypes方法刪除,添加一個根據用戶姓名獲得用戶信息的方法,如下:

// IUserManager.aidl
package com.example.aidltest;

import com.example.adiltest.User;

interface IUserManager {

   User getUser(String name);
}

在裏面我顯示的 import 了User這個AIDL文件,即使它們在同一個包內,並聲明瞭一個getUser方法,這個方法將會在服務端實現,然後在客戶端調用(RPC)。

三、根據AIDL接口生成 對應的Binder類

有了AIDL接口後我們需要根據AIDL接口生成客戶端和服務端對應的Binder類,有兩種方式生成,一種是通過SDK自動生成,另外一種是我們自己手動編碼實現,其中能夠進行手動編碼實現的前提是基於對SDK自動生成的各種Binder類的充分理解,下面我們先來介紹SDK自動生成的Binder類。

3.1、SDK自動生成

我們在AS導航欄 Build -> ReBuild Project,SDK就會替我們在 app\build\generated\aidl_source_output_dir\debug\compileDebugAidl\out\包名 下生成一個IUserManager.java,它就是根據IUserManager.aidl文件生成的,裏面沒有縮進,所以看起來不習慣,使用快捷鍵ctrl+alt+L,格式化一下代碼,如下:

//IUserManager.java

/**
 * 1、IUserManager接口,getUser方法定義在其中
 **/
public interface IUserManager extends android.os.IInterface {
   
    /**
     * 1、抽象類Stub,需要在遠程服務端實現
     */
    public static abstract class Stub extends android.os.Binder implements com.example.aidltest.IUserManager {
        private static final java.lang.String DESCRIPTOR = "com.example.aidltest.IUserManager";

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }
        
        public static com.example.aidltest.IUserManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.aidltest.IUserManager))) {
                return ((com.example.aidltest.IUserManager) iin);
            }
            return new com.example.aidltest.IUserManager.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 {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_getUser: {//case TRANSACTION_getUser分支
                    data.enforceInterface(descriptor);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    //調用getUser方法的具體實現
                    com.example.aidltest.User _result = this.getUser(_arg0);
                    reply.writeNoException();
                    if ((_result != null)) {
                        reply.writeInt(1);
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        /**
         * 2、代理類Proxy,客戶端使用
         */
        private static class Proxy implements com.example.aidltest.IUserManager {
            
            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 com.example.aidltest.User getUser(java.lang.String name) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                com.example.aidltest.User _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(name);
                    //傳進了TRANSACTION_getUser字段
                    mRemote.transact(Stub.TRANSACTION_getUser, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        _result = com.example.aidltest.User.CREATOR.createFromParcel(_reply);
                    } else {
                        _result = null;
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_getUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }
    
 	public com.example.aidltest.User getUser(java.lang.String name) throws android.os.RemoteException;
}

這個文件有3個主要的類:

1、IUserManager接口

它聲明瞭IUserManager.aidl定義的getUser方法,繼承自IInterface。

2、IUserManager.Stub抽象類

IUserManager的靜態內部類,它繼承自Binder,說明它是一個 Binder 本地對象(Binder下面介紹),它雖然實現了IUserManager接口,但是它繼續聲明爲一個抽象類,並沒有實現IUserManager接口中的getUser方法,表明子類需要實現getUser方法,返回具體的User信息,而服務端將會實現這個Stub抽象類。

**3、IUserManager.Stub.Proxy代理類 **

IUserManager.stub的靜態內部類,它實現了IUserManager接口,並且實現了getUser方法,但是裏面只是把數據裝進data這個Parcel對象,通過mRemote的transact方法發送給服務端,接着用reply這個Parcel對象等待服務端數據的返回,這一切都是通過mRemote這個IBinder對象進行,mRemote代表着Binder對象的本地代理(IBinder下面介紹),mRemote會通過Binder驅動來完成與遠程服務端的Stub的通信。

可以看到Stub類和Stub.Proxy類都實現了IUserManager接口,這就是一個典型的代理模式,它們的getUser方法有着不同的實現,Stub類它將會在遠程的服務端完成getUser方法的具體實現,而Stub.Proxy類是本地客戶端的一個代理類,它已經替我們默認的實現了getUser方法,該方法裏面通過mRemote這個Binder引用的transact方法把請求通過驅動發送給服務端,我們注意到mRemote發送請求時還傳進了TRANSACTION_getUser這個代表着getUser方法的標識名,這表示客戶端告訴服務端我要調用getUser這個方法,當驅動把請求轉發給服務端後,服務端的Stub類的onTransact方法就會回調,它裏面有一個switch語句,根據code來調用不同的方法,這時它就會走到case TRANSACTION_getUser這個分支,然後調用getUser方法的在服務端的具體實現,如果有返回值的話,還會通過reply返回給客戶端,這樣就通過Binder驅動完成了一次遠程方法調用(RPC)。

這裏要注意的是客戶端通過mRemote的transact方法把請求發送給客戶端之後,這時會阻塞UI線程等待服務端的返回,而服務端的onTransact方法回調時,服務端的getUser方法會被回調,這時服務端的getUser方法是運行在服務端Binder線程池中,所以如果此時有UI操作需要回到UI線程再進行UI操作。

我們還注意到IUserManager接口繼承了IInterface,IUserManager.Stub繼承自Binder,它們是幹什麼的?我們來認識一下:

IInterface

這是一個接口,用來表示服務端提供了哪些服務,如果服務端需要暴露調用服務的方法給客戶端使用,就一定要繼承這個接口,它裏面有個asBinder方法,用於返回當前的Binder對象。

IBinder

這時一個跨進程通信的Base接口,它聲明瞭跨進程通信需要實現的一系列抽象方法,實現了這個接口就說明可以進行跨進程通信,Binder和BinderProxy都繼承了這個接口。

Binder

代表的是 Binder 本地對象(Binder實體),它繼承自IBinder,所有本地對象都要繼承Binder,Binder中有一個內部類BinderProxy,它也繼承自IBinder,它代表着Binder對象的本地代理(Binder引用),Binder實體只存在於服務端,而Binder引用則遍佈於各個客戶端。

接下來我們動手實踐一下,首先在服務端RemoteService中,我們要這樣做:

public class RemoteService extends Service {

    //1、實現Stub類中的getUser方法
    private IUserManager.Stub mBinder = new IUserManager.Stub() {
        @Override
        public User getUser(String name) throws RemoteException {
            //這裏只是簡單的返回了一個用戶名爲name,密碼爲123456的用戶實例
            return new User(name, "123456");
        }
    };

    public RemoteService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        //2、在onBinder方法中返回Stub類的實現,Stub類繼承自Binder,Binder實現了IBinder,這樣返回是沒問題的
        return mBinder;
    }
}

在服務端我們需要實現Stub類中的getUser方法,然後在onBinder方法中返回Stub類的實現,這樣客戶端綁定服務時就會收到這個Stub類的Binder引用。

然後在客戶端ClientActivity中,我們要這樣做:

public class ClientActivity extends AppCompatActivity {

    private static final String TAG = ClientActivity.class.getSimpleName();

    //1、創建ServiceConnection
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "onServiceConnected, 與服務端連接成功!");
            
            //把服務端返回的Binder引用,通過Stub.asInterface方法包裝成本地代理類IUserManager.Stub.Proxy,Proxy類實現了IUserManager,所以這樣寫是沒問題的
            IUserManager userManager = IUserManager.Stub.asInterface(service);
            
            try {
                //通過本地代理對象遠程調用服務端的方法
                User user = userManager.getUser("rain");
                
                Log.d(TAG, "onServiceConnected,向服務端獲取用戶信息成功,User = ["
                        + "name = " + user.getName()
                        + "password = " + user.getPassword());
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG, "onServiceDisconnected, 與服務端斷開連接");

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //2、啓動並通過ServiceConnection綁定遠程服務
        bindService(
                new Intent(this, RemoteService.class),
                mServiceConnection,
                Context.BIND_AUTO_CREATE
        );
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //3、解綁服務
        unbindService(mServiceConnection);
    }
}

在服務端我們首先要創建ServiceConnection,然後通過ServiceConnection來綁定RemoteService,在成功綁定後,ServiceConnection的onServiceConnected方法就會回調,它的第二個輸入參數就是RemoteService在onBind方法返回的Stub類的Binder引用,我們拿到這個引用後,就可以通過通過Stub.asInterface方法轉換爲本地代理類Stub.Proxy,然後調用它的getUser方法,Proxy的getUser方法會遠程調用RemoteService的getUser方法,方法返回後,在log中打印出User的信息,最後,活動結束,我們記得解綁服務,這個過程和上面介紹的一次RPC過程是一樣的。

我們發現完成一次進程間的通信是非常的簡單,這就好像只是簡單的調用一個對象的方法,但其實這都得益於Binder在底層爲我們做了更多工作,我們上層使用得有多簡單,它得底層實現就有多複雜,上面的一次進程間通信,可以簡單的用下圖表示:

其中ServiceManager就是根據Binder的名字查找Binder引用並返回,如果你對Binder的通信架構有一定的瞭解,理解這個就不難,對象轉換就是完成Binder實體 -> Binder引用,Binder引用 -> Binder實體的轉換,在java層繼承自Binder的都代表Binder本地對象,即Binder實體,而Binder類的內部類BinderProxy就代表着Binder對象的本地代理,即Binder引用,這兩個類都繼承自IBinder, 因而都具有跨進程傳輸的能力,在跨越進程的時候,Binder 驅動會自動完成這兩個對象的轉換。

我們重點講一下圖中的第3步:將Binder引用賦值給Proxy的mRemote字段,Proxy就是前面介紹的Stub.Proxy,所以接着我們看看**IUserManager.Stub.asInterface(IBinder)**方法是如何把服務端返回的Binder引用賦值給本地的代理類Proxy的mRemote字段,asInterface方法如下:

//IUserManager.Stub.java
public static com.example.aidltest.IUserManager asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    //根據DESCRIPTOR調用IBinder的queryLocalInterface方法
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.example.aidltest.IUserManager))) {
        return ((com.example.aidltest.IUserManager) iin);
    }
    return new com.example.aidltest.IUserManager.Stub.Proxy(obj);
}

//Binder
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
    if (mDescriptor != null && mDescriptor.equals(descriptor)) {
        //mOwner等於Stub類實例
        return mOwner;
    }
    return null;
}

//Binder#BinderProxy
public IInterface queryLocalInterface(String descriptor) {
    return null;
}

可以發現它裏面根據DESCRIPTOR標識調用IBinder的queryLocalInterface方法在查找一些什麼,DESCRIPTOR是什麼?DESCRIPTOR是Binder實體的唯一標識,一般用當前的Binder的類名錶示,它定義在Stub類中,如本文的Stub的 DESCRIPTOR = "com.example.aidltest.IUserManager",前面已經講過Binder和BinderProxy都繼承自IBinder,所以它們的queryLocalInterface有不同的實現,我們看到BinderProxy的直接返回null;而Binder的需要和自己的DESCRIPTOR比較,如果相同就返回mOwner,否則返回null,其中mOwner就等於Stub類實例,在Stub類構造的時候賦值,如下:

public static abstract class Stub extends android.os.Binder implements com.example.aidltest.IUserManager {
    
    private static final java.lang.String DESCRIPTOR = "com.example.aidltest.IUserManager";

    public Stub() {
        this.attachInterface(this, DESCRIPTOR);
    }

    public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }
    
    //...
}

這說明了如果queryLocalInterface的返回不爲空,iin != null,表示obj是Binder實體(Stub的子類),客戶端和服務端在同一個進程,asInterface方法返回的就是Binder實體;如果queryLocalInterface的返回爲空,iin == nul,表示obj實際上是Binder引用,客戶端和服務端在不同的進程,asInterface構造一個Proxy對象返回,並把Binder引用通過構造傳了進去,我們看Proxy的構造函數,如下:

private static class Proxy implements com.example.aidltest.IUserManager {
    private android.os.IBinder mRemote;

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

可以看到,傳進去的Binder引用賦值給了mRemote字段,所以Proxy中的mRemote就是Binder引用,客戶端就是通過這個mRemote來和服務端通信。

也就是說,如果在同一個進程,asInterface返回的是Stub的實現類,因爲不存在跨進程調用,直接調用該方法就行,如果在不同進程,asInterface返回的是Proxy對象,客戶端調用Proxy中的同名方法,通過mRemote的transact方法掛起當前線程等待服務端返回,服務端收到請求後響應返回數據。

到這裏,相信大家在SDK把幫助下已經會使用AIDL來完成簡單的進程間通信,接下來通過手動編碼實現。

3.2、手動編碼實現

我們發現使用AIDL系統會自動的幫我們生成上述代碼,是爲了方便我們的開發,系統根據AIDL文件生成的java文件格式是固定,我們完全可以拋開AIDL直接手寫對應的Binder類,下面我們本着單一原則把原本IUserManager.java的裏面的Stub類和Stub類中的Proxy類獨立出來,所以我們總共要寫3個類,分別是:IUserManager、Stub、Proxy。

1、聲明一個繼承自IInterface的接口

聲明一個繼承自IInterface的接口,在裏面定義我們想要讓客戶端調用的方法,如下:

public interface IUserManager extends IInterface {
    User getUser(String name);
}

2、聲明服務端的Stub類

Stub類需要繼承Binder,表明它是一個Binder本地對象,它還需要實現IUserManager接口,但是繼續聲明爲抽象類,不需要實現IUserManager的getUser方法,接着我們做以下幾步:

1、在裏面定義一個字符串DESCRIPTOR,表示Binder實體的唯一標識,用當前的Stub類的類名錶示,並把它的可見修飾符改爲public,待會在Proxy需要用到.

2、在構造函數中把this 和 DESCRIPTOR字符串 attach 給父類Binder中的mOwner和mDescriptor字段.

3、定義一個TRANSACTION_getUser整型數值代表着getUser方法的標識名,賦值格式照抄自動生成的Stub類的TRANSACTION_getUser.

4、定義一個asInterface靜態方法,裏面的內容實現照抄自動生成的Stub類的asInterface方法,需要注意裏面的IUserManager接口需要換成我們剛剛定義的IUserManager接口.

5、最後重寫IInterface的asBinder方法和Binder的onTransact方法,裏面的內容實現照抄自動生成的Stub類的asBinder和onTransact方法.

最終這個Stub類如下:

public abstract class Stub extends Binder implements IUserManager {

    public static final String DESCRIPTOR = "com.example.aidltest.Stub";//這裏改成com.example.aidltest.Stub

    static final int TRANSACTION_getUser = (IBinder.FIRST_CALL_TRANSACTION + 0);

    public Stub() {
        this.attachInterface(this, DESCRIPTOR);
    }

    public static IUserManager asInterface(android.os.IBinder obj) {//導入我們自定義的IUserManager
        if ((obj == null)) {
            return null;
        }
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof IUserManager))) {
            return (IUserManager) iin;
        }
        return new 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 {
        java.lang.String descriptor = DESCRIPTOR;
        switch (code) {
            case INTERFACE_TRANSACTION: {
                reply.writeString(descriptor);
                return true;
            }
            case TRANSACTION_getUser: {
                data.enforceInterface(descriptor);
                java.lang.String _arg0;
                _arg0 = data.readString();
                com.example.aidltest.User _result = this.getUser(_arg0);
                reply.writeNoException();
                if ((_result != null)) {
                    reply.writeInt(1);
                    _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                } else {
                    reply.writeInt(0);
                }
                return true;
            }
            default: {
                return super.onTransact(code, data, reply, flags);
            }
        }
    }
}

3、聲明客戶端的Proxy類

Proxy類只需要實現IUserManager接口,並且實現IUserManager中的getUser方法,接着我們做以下幾步:

1、定義一個IBinder類型的mRemote字段,並在構造函數中賦值.

2、實現IUserManager中的getUser方法和IInterface的asBinder方法,裏面的內容實現照抄自動生成的Stub.Proxy的getUser方法和asBinder方法,需要注意getUser中的一些字段需要導入我們剛剛在Stub類中定義的字段.

最終這個Proxy類如下:

public class Proxy implements IUserManager {

    private IBinder mRemote;

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

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

    @Override
    public com.example.aidltest.User getUser(java.lang.String name) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        com.example.aidltest.User _result;
        try {
            _data.writeInterfaceToken(Stub.DESCRIPTOR);//這裏引用我們自己寫的Stub的DESCRIPTOR
            _data.writeString(name);
            mRemote.transact(Stub.TRANSACTION_getUser, _data, _reply, 0);//這裏引用我們自己寫的Stub的TRANSACTION_getUser
            _reply.readException();
            if ((0 != _reply.readInt())) {
                _result = com.example.aidltest.User.CREATOR.createFromParcel(_reply);
            } else {
                _result = null;
            }
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }

}

最終整個工程目錄如下:

4、使用

使用就很簡單了,只需要把上面自動生成的IUserManager、IUserManager.Stub、IUserManager.Stub.Proxy替換成我們自己寫的IUserManager、Stub、Proxy就行,就不再貼代碼了,輸出如下:

 

四、學完AIDL能幹什麼

在android8.0之後Activity的有關進程間的通信都是通過AIDL來實現,Android根據IActivityManager.aidl文件來生成進程間通信所需的Binder類,如ApplicationThread在AMS的本地代理,AMS在ActivityThread的本地代理,當我們通過startActiivty發起Activity的啓動請求時,ActivityThread就會通過AMS本地代理調用AMS的相應方法,當Actiivty在AMS準備好後,AMS就會通過ActivityThread本地代理回調應用進程的Activity的生命週期方法,這裏就不在多述了,這個過程如下:

主要是通過這個例子說明,學習完AIDL後,能夠幫助我們更好的理解系統源碼有關跨進程的一些術語,類等,通過AIDL也能更好的加深我們對Android進程間通信的原理的理解,也掌握了一種進程間通信的方式。

好了,文章到這裏就結束瞭如果你覺得文章還算有用的話,不妨把它們推薦給你的朋友。

漫漫開發之路,我們只是其中的一小部分……只有不斷的學習、進階,纔是我們的出路!纔跟得上時代的進步!

今年年初我花一個月的時間收錄整理了一套知識體系,如果有想法深入的系統化的去學習的,可以私信我【學習】,我會把我收錄整理的資料都送給大家,幫助大家更快的進階。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章