使用AIDL來進行進程間通訊

前言

AIDL它是Android衆多進程間通訊方式中的一種,底層是Binder機制的實現,因此想要讀懂AIDL自動生成的代碼中各個類的做用,就必須對Binder有必定的瞭解,其實不止AIDL,Android進程間通訊的大多數方式的底層都是Binder機制,本文主要介紹AIDL的使用,因此不會介紹Binder機制的原理,關於Binder機制的原理,我推薦下面的一篇文章,零基礎也能看懂:html

寫給 Android 應用工程師的 Binder 原理剖析java

看完上面的文章你也就能對Binder原理的實現有大概的瞭解,對於咱們Android應用開發也就足夠了,若是你還不知足,想從底層和源碼瞭解Binder機制,你能夠閱讀下面的兩個連接:android

Android Bander設計與實現git

Binder系列—開篇github

上面兩個系列就是從設計和源碼的角度去解讀Binder,有點深刻。好了,對Binder有一個大致上的認識後,接下來咱們就要經過AIDL的使用來完成Android進程間通訊的實踐。數組

Android進程間通訊的方式

在介紹AIDL以前,咱們先來看一下Android中除了AIDL還有哪些進程間通訊的方式:網絡

一、Bundle架構

Bundle實現了Parcelable,因此在Android中咱們能夠經過Intent在不一樣進程間傳遞Bundle數據。併發

可是在Intent 傳輸數據的過程當中,用到了 Binder,Intent中的數據,即Bundle數據,會做爲 Parcel 存儲在Binder 的事務緩衝區(Binder transaction buffer)中的對象進行傳輸,而這個 Binder 事務緩衝區具備一個有限的固定大小,約爲1MB,並且這個1Mb並非當前進程所獨享,而是全部進程共享的,因此因爲1Mb的限制,Bundle不能存放大量的數據,否則會報TransactionTooLargeException,而且Bundle中存放的數據也要求可以被序列化,因此Bundle只適用於數據量小和簡單的進程間通訊。app

二、文件共享

兩個進程間經過讀寫同一個文件來交換數據。

可是因爲Android容許併發的對同一個文件讀寫,因此若是兩個進程同時的對這個文件寫,就會出現問題,因此這適用於對數據同步要求不高的進程間通訊。

三、ContentProvider

ContentProvider是Android中專門用於不一樣應用之間,即不一樣進程之間進行數據共享的方式,它的底層實現是Binder。

ContentProvider是四大組件之一,它的使用比較簡單,咱們只須要繼承自ContenProvider,並重寫它的六個方法:onCreate、query、updata、insert、delete和getType,其中onCreate用於初始化,getType用於返回一個Uri請求表明的MIME類型,剩下的都是CRUD操做,除了onCreate方法運行在主線程,其餘方法都運行在Binder線程池,而後在另一個進程中註冊這個ContentProvider,在本進程的Activity中經過getContentResolver()得到ContentResolver後,再經過ContentResolver來完成對這個ContentProvider的CRUD操做。

四、套接字(Socket)

一種傳統的Linux IPC方式,除了用於不一樣進程之間還能夠用於不一樣機器之間(經過網絡傳輸)的通訊,可是它的傳輸效率也是很是的低。

五、Messenger

經過Messenger能夠在不一樣進程之間傳遞Message對象,Message中能夠放入咱們須要傳遞的數據,它的底層實現是AIDL。

可是Messenger在服務端的Handler是以串行的方式處理來自客戶端的Message,因此若是有大量的併發請求,Messenger就效率低下,因此Messenger適用於數據併發量低的進程間通訊。

六、AIDL

也就是本文的主角,它的底層實現是Binder。

能夠看到Android中進程間通訊的方式中,除了文件共享和Socket,底層都是須要經過Binder來傳輸數據,可見Binder對Android的進程間通訊來講是多麼的重要。

進程間通訊的準備

一、 序列化

Android中要在進程間傳輸的數據都要是可序列化的,序列化就是把對象轉換成二進制(字節流),從而能夠存儲到存儲設備或者經過網絡進行傳輸,有序列化,就有反序列,反序列化就是把二進制(字節流)轉化爲對象,其中8種基本數據類型默承認以在進程間傳輸,沒有序列化的概念,序列化的做用對象是對象。在Android中,對象經過實現Serializable或Parcelable接口來實現序列化,下面簡單介紹一下它們之間使用和區別:

1.一、Serializable

Serializable是java中提供的一個序列化接口,它是一個空接口,對象實現這個Serializable接口,就標記這個對象是可序列化的,而後咱們經過ObjectOutputStream的writeObject方法就能完成對象的序列化,以下:

User user = new User();
ObjectOutputStream os = new  ObjectOutputStream(new FileOutputStream("D://test.txt"));
os.writeObject(user);
os.close();
複製代碼

同理,經過ObjectOutputStream的readObject方法就能完成對象的反序列化,以下:

ObjectOutputStream in = new  ObjectInputStream(new FileInputStream("D://test.txt"));
User user = in.readObject();
in.close();
複製代碼

若是你在序列化這個對象以後有可能會改變這個對象的類結構,例如爲類添加新的字段,這時在序列化這個對象以前你就要爲這個對象的類加上一個serialVersionUID參數,以下:

public class User{
    //serialVersionUID取什麼值不要緊,只要保持不變就行
    private static final long serialVersionUID = 1L;
}
複製代碼

若是你確保這個對象的類結構在序列化以後是一直不變的,那麼這個serialVersionUID能夠忽略,爲何會這樣呢?這是由於在序列化的時候,系統會把當前類的serialVersionUID寫入到序列化的文件中,當反序列化時系統就會去檢測文件中的serialVersionUID是否和當前類的serialVersionUID相同,若是相同就說明序列化的類和當前類的版本是相同的,這時候系統能夠正確的反序列化,若是不一致,就說明序列化的類和當前類相比發生了某些變化,例如當前類添加了新的字段,這時就會反序列化失敗,拋出異常。因此若是你手動指定了serialVersionUID的值,並一直保持不變,就算你改變了類的結構(不要改變類名和變量類型),序列化和反序列化時二者的serialVersionUID都是相同的,能夠正常的進行反序列化,相反,若是你沒有指定serialVersionUID的值,系統會根據類的結構自動生成它的hash值做爲serialVersionUID的值,這時你改變了類的結構,在進行序列化和反序列化時二者生成的serialVersionUID不相同,這時反序列化就會失敗,因此當你沒有指定serialVersionUID的值,你要確保這個對象的類結構在序列化以後是一直不變。固然大多數狀況下咱們仍是會指定serialVersionUID的值,以防咱們不當心修改類的結構致使沒法恢復對象。

這裏只是列舉了用Serializable實現對象序列化最簡單的使用,其實咱們能夠經過transient關鍵字控制哪些字段不被序列化,還有靜態成員不屬於對象,不會參與序列化過程,還能夠手動控制Serializable對象的序列化和反序列化過程,還關於Serializable更多使用方式能夠看Java對象表示方式1:序列化、反序列化和transient關鍵字的做用

1.二、Parcelable

Parcelable是Android提供的一個接口,一個對象只要實現了這個接口,就能夠實現序列化,以下:

public class User implements Parcelable {

    private final String name;
    private final String password;

    public User(String name, String password) {
        this.name = name;
        this.password = password;
    }
    
    public String getName() {
        return name == null ? "" : name;
    }

    public String getPassword() {
        return password == null ? "" : password;
    }

    //下面就是實現Parcelable接口須要實現的方法
    
    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.name);
        dest.writeString(this.password);
    }

    public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() {
        @Override
        public User createFromParcel(Parcel source) {
            return new User(source);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };
    
    protected User(Parcel in) {
        this.name = in.readString();
        this.password = in.readString();
    }
}
複製代碼

User中須要序列化的字段都會包裝進Parcel中,當反序列化時,從Parcel中讀取數據,Parcel內部包裝了可序列化的數據,能夠在進程間傳輸,其中describeContents方法用來完成內容描述,通常返回0,writeToParcel方法用來實現序列化,CREATOR對象的內部方法用來實現反序列化,其中createFromParcel方法用來從序列化後的對象中建立原始對象,newArray方法用來建立指定長度的原始對象數組。

Parcelable的用法相比Serializable複雜了一點,若是每次須要序列化一個對象,都要寫這麼多重複樣本代碼,會有點累,這裏我推薦一個插件android-parcelable-intellij-plugin,專門用於自動生成這些重複樣本代碼。

1.三、Serializable和Parcelable的區別

Serializable和Parcelable均可以實現序列化,用於進程間的數據傳遞,它們之間有什麼區別呢?在編碼上,Serializable使用簡單,而Parcelable稍顯複雜;在效率上,Serializable的開銷很大,由於它只是一個空接口,咱們無需實現任何方法,Java便會對這個對象進行序列化操做,這是由於java會經過反射建立所需方法,致使序列化的過程較慢,而Parcelable的效率高,它的速度比Serializable快十倍,經過把一個完整的對象進行分解,而分解後的每一部分都是Intent所支持的數據類型,這樣也就實現了傳遞對象的功能;在使用場景上,Parcelable主要用在內存序列化上,它不能使用在要將數據序列化在磁盤上的狀況,由於在外界有變化的狀況下,Parcelable不能很好的保證數據的持續性,因此若是須要序列化到磁盤或經過網絡傳輸建議使用Serializable,儘管它的效率低點。

綜上所述,在Android開發中,若是有序列化的要求,咱們多點考慮使用Parcelable,畢竟它是Android自帶的,雖然它使用稍顯複雜,可是有插件的幫助,加上它的效率高,就略勝Serializable了。

二、開啓多進程模式

既然是進程間通訊,就必定會涉及到兩個或兩個進程以上,咱們知道,在Android中,啓動一個應用就表明啓動了一個進程,但其實除了啓動多個應用外,其實在一個應用中,也能夠存在多個進程,只要你爲四大組件在AndroidMenifest中指定「android:process」屬性就行,好比我但願ClientActivity啓動的時候,運行在獨立的進程,我就會在AndroidMenifest中這樣寫,以下:

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

<activity android:name=".ClientActivity" android:process="com.example.aidltest.client"/>
複製代碼

能夠看到我只是簡單的ClientActivity指定了「android:process」屬性,這樣ClientActivity在啓動的時候就會運行在進程名爲com.example.aidltest.client的獨立進程上,而MainActivity啓動時,就會運行在默認進程上,默認進程的進程名爲當前包名即com.example.aidltest。

須要注意的是,此時這兩個Activity雖然在同一個應用,可是卻不在同一個進程,因此它們是不共享內存空間的,Android會爲每個進程分配一個獨立的虛擬機,不一樣的虛擬機在內存分配上有不一樣的地址空間,因此MainActivity和ClientActivity的內存地址空間不同,它們要訪問對方的數據的時候,都要經過進程間通訊的方式來進行。

接下來爲了方便後面的講解,我把ClientActivity指定爲主活動,把MainActivity刪除,再建立一個Server,併爲它指定process屬性,以下:

<activity android:name=".ClientActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

<service android:name=".RemoteService" android:enabled="true" android:exported="true" android:process="com.example.aidltest.remote"/>
複製代碼

ClientActivity啓動時會運行在默認的進程上,RemoteService啓動時會運行在名爲com.example.aidltest.remote的進程上,它們在本文分別表明爲本地客戶端和遠程服務端。

使用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.一、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

/** * 一、IUserManager接口,getUser方法定義在其中 **/
public interface IUserManager extends android.os.IInterface {
   
    /** * 一、抽象類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);
                }
            }
        }

        /** * 二、代理類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個主要的類:

一、IUserManager接口

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

二、IUserManager.Stub抽象類

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

**三、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 {

    //一、實現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) {
        //二、在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();

    //一、建立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);
        //二、啓動並經過ServiceConnection綁定遠程服務
        bindService(
                new Intent(this, RemoteService.class),
                mServiceConnection,
                Context.BIND_AUTO_CREATE
        );
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //三、解綁服務
        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.二、手動編碼實現

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

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

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

public interface IUserManager extends IInterface {
    User getUser(String name);
}
複製代碼

二、聲明服務端的Stub類

Stub類須要繼承Binder,代表它是一個Binder本地對象,它還須要實現IUserManager接口,可是繼續聲明爲抽象類,不須要實現IUserManager的getUser方法,接着咱們作如下幾步:

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

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

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

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

五、最後重寫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);
            }
        }
    }
}
複製代碼

三、聲明客戶端的Proxy類

Proxy類只須要實現IUserManager接口,而且實現IUserManager中的getUser方法,接着咱們作如下幾步:

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

二、實現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;
    }

}
複製代碼

最終整個工程目錄以下:

四、使用

使用就很簡單了,只須要把上面自動生成的IUserManager、IUserManager.Stub、IUserManager.Stub.Proxy替換成咱們本身寫的IUserManager、Stub、Proxy就行,就再也不貼代碼了,輸出以下:

學完AIDL能幹什麼

日常在Android中應用到進程間通訊的場景很是的少,但這並非說AIDL沒有用,一個最直觀的應用就是在閱讀Android系統源碼的時候,例如Activity的啓動流程:

Activity的啓動流程(1)

Activity的啓動流程(2)

在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進程間通訊的原理的理解,也掌握了一種進程間通訊的方式。

結語

本文簡單的介紹了一下Android幾種進程間通訊的方式,而後經過SDK自動生成AIDL代碼來理解了一下生成的代碼中各個類的做用和關係,還根據自動生成AIDL代碼來手動實現了一遍簡單的跨進程通訊,加深理解,掌握了一些基礎AIDL知識,可能會有些不全面,可是足夠基本使用,想要了解更全面的AIDL知識,最好的途徑仍是參閱官方文檔:Android 接口定義語言 (AIDL)

本文源碼地址

參考資料:

Android系統中Parcelable和Serializable的區別

關於Binder,做爲應用開發者你須要知道的所有

相關文章
相關標籤/搜索