Android 進階7:進程通訊之 AIDL 的使用

讀完本文你將瞭解:html

AIDL 是什麼
AIDL 支持的數據類型
AIDL 如何編寫
AIDL 實例
建立 AIDL
編寫服務端代碼
編寫客戶端代碼
運行結果
總結
代碼地址
Thanks
記得 2015 年實習面試,筆試題裏就有這道題:請介紹下 AIDL。java

當時的我是懵逼的,只好老老實實空着。沒想到後來面試時面試官大哥嘿嘿一笑說他也沒用過這玩意,真是夠實誠的。android

筆試完查了這個知識點,似懂非懂也沒深究。去年看《安卓開發藝術探索》時也學了這部份內容,可是可能當時水平不夠,或者只是看起來努力,沒有真正理解精髓,沒多久就又忘了個七八成。面試

此次複習,仍是老老實實敲出來,總結成文字吧,方便之後回顧。編程

AIDL 是什麼
AIDL(Android 接口定義語言) 是 Android 提供的一種進程間通訊 (IPC) 機制。dom

咱們能夠利用它定義客戶端與服務使用進程間通訊 (IPC) 進行相互通訊時都承認的編程接口。ide

在 Android 上,一個進程一般沒法訪問另外一個進程的內存。 儘管如此,進程須要將其對象分解成操做系統可以識別的原語,並將對象編組成跨越邊界的對象。ui

編寫執行這一編組操做的代碼是一項繁瑣的工做,所以 Android 會使用 AIDL 來處理。this

經過這種機制,咱們只須要寫好 aidl 接口文件,編譯時系統會幫咱們生成 Binder 接口。操作系統

AIDL 支持的數據類型
共 4 種:

Java 的基本數據類型
List 和 Map 
元素必須是 AIDL 支持的數據類型
Server 端具體的類裏則必須是 ArrayList 或者 HashMap
其餘 AIDL 生成的接口
實現 Parcelable 的實體
AIDL 如何編寫
AIDL 的編寫主要爲如下三部分:

建立 AIDL 
建立要操做的實體類,實現 Parcelable 接口,以便序列化/反序列化
新建 aidl 文件夾,在其中建立接口 aidl 文件以及實體類的映射 aidl 文件
Make project ,生成 Binder 的 Java 文件
服務端 
建立 Service,在其中建立上面生成的 Binder 對象實例,實現接口定義的方法
在 onBind() 中返回
客戶端 
實現 ServiceConnection 接口,在其中拿到 AIDL 類
bindService()
調用 AIDL 類中定義好的操做請求
AIDL 實例
下面以實例代碼演示一個 AIDL 的編寫。

1.建立 AIDL
①建立要操做的實體類,實現 Parcelable 接口,以便序列化/反序列化


package net.sxkeji.shixinandroiddemo2.bean;

import android.os.Parcel;
import android.os.Parcelable;

public class Person implements Parcelable {
    private String mName;

    public Person(String name) {
        mName = name;
    }

    protected Person(Parcel in) {
        mName = in.readString();
    }

    public static final Creator<Person> CREATOR = new Creator<Person>() {
        @Override
        public Person createFromParcel(Parcel in) {
            return new Person(in);
        }

        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "mName='" + mName + '\'' +
                '}';
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
實現 Parcelable 接口是爲了後序跨進程通訊時使用。

關於 Parcelable 能夠看個人這篇文章 Android 進階6:兩種序列化方式 Serializable 和 Parcelable。

注意 實體類所在的包名。

②新建 aidl 文件夾,在其中建立接口 aidl 文件以及實體類的映射 aidl 文件

在 main 文件夾下新建 aidl 文件夾,使用的包名要和 java 文件夾的包名一致:

先建立實體類的映射 aidl 文件,Person.aidl:

// Person.aidl
package net.sxkeji.shixinandroiddemo2.bean;

//還要和聲明的實體類在一個包裏
parcelable Person;
1
2
3
4
5
在其中聲明映射的實體類名稱與類型

注意,這個 Person.aidl 的包名要和實體類包名一致。

而後建立接口 aidl 文件,IMyAidl.aidl:

// IMyAidl.aidl
package net.sxkeji.shixinandroiddemo2;

// Declare any non-default types here with import statements
import net.sxkeji.shixinandroiddemo2.bean.Person;

interface IMyAidl {
    /**
     * 除了基本數據類型,其餘類型的參數都須要標上方向類型:in(輸入), out(輸出), inout(輸入輸出)
     */
    void addPerson(in Person person);

    List<Person> getPersonList();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
在接口 aidl 文件中定義未來要在跨進程進行的操做,上面的接口中定義了兩個操做:

addPerson: 添加 Person
getPersonList:獲取 Person 列表
須要注意的是:

非基本類型的數據須要導入,好比上面的 Person,須要導入它的全路徑。 
這裏的 Person 我理解的是 Person.aidl,而後經過 Person.aidl 又找到真正的實體 Person 類。
方法參數中,除了基本數據類型,其餘類型的參數都須要標上方向類型 
in(輸入), out(輸出), inout(輸入輸出)
③Make Project ,生成 Binder 的 Java 文件

AIDL 真正的強大之處就在這裏,經過簡單的定義 aidl 接口,而後編譯,就會爲咱們生成複雜的 Java 文件。

點擊 Build -> Make Project,而後等待構建完成。

而後就會在 build/generated/source/aidl/你的 flavor/ 下生成一個 Java 文件:

如今咱們有了跨進程 Client 和 Server 的通訊媒介,接着就能夠編寫客戶端和服務端代碼了。

咱們先跑通整個過程,這個文件的內容下篇文章介紹。

2.編寫服務端代碼
建立 Service,在其中建立上面生成的 Binder 對象實例,實現接口定義的方法;而後在 onBind() 中返回

建立未來要運行在另外一個進程的 Service,在其中實現了 AIDL 接口中定義的方法:

public class MyAidlService extends Service {
    private final String TAG = this.getClass().getSimpleName();

    private ArrayList<Person> mPersons;

    /**
     * 建立生成的本地 Binder 對象,實現 AIDL 制定的方法
     */
    private IBinder mIBinder = new IMyAidl.Stub() {

        @Override
        public void addPerson(Person person) throws RemoteException {
            mPersons.add(person);
        }

        @Override
        public List<Person> getPersonList() throws RemoteException {
            return mPersons;
        }
    };

    /**
     * 客戶端與服務端綁定時的回調,返回 mIBinder 後客戶端就能夠經過它遠程調用服務端的方法,即實現了通信
     * @param intent
     * @return
     */
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        mPersons = new ArrayList<>();
        LogUtils.d(TAG, "MyAidlService onBind");
        return mIBinder;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
上面的代碼中,建立的對象是一個 IMyAidl.Stub() ,它是一個 Binder,具體爲何是它咱們下篇文章介紹。

別忘記在 Manifest 文件中聲明:

<service
    android:name="net.sxkeji.shixinandroiddemo2.service.MyAidlService"
    android:enabled="true"
    android:exported="true"
    android:process=":aidl"/>
1
2
3
4
5
服務端實現了接口,在 onBind() 中返回這個 Binder,客戶端拿到就能夠操做數據了。

3.編寫客戶端代碼
這裏咱們以一個 Activity 爲客戶端。

①實現 ServiceConnection 接口,在其中拿到 AIDL 類

private IMyAidl mAidl;

private ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        //鏈接後拿到 Binder,轉換成 AIDL,在不一樣進程會返回個代理
        mAidl = IMyAidl.Stub.asInterface(service);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        mAidl = null;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
在 Activity 中建立一個服務鏈接對象,在其中調用 IMyAidl.Stub.asInterface() 方法將 Binder 轉爲 AIDL 類。

②接着綁定服務

Intent intent1 = new Intent(getApplicationContext(), MyAidlService.class);
bindService(intent1, mConnection, BIND_AUTO_CREATE);
1
2
要執行 IPC,必須使用 bindService() 將應用綁定到服務上。

注意:

5.0 之後要求顯式調用 Service,因此咱們沒法經過 action 或者 filter 的形式調用 Service,具體內容能夠看這篇文章 Android 進階:Service 的一些細節。

③拿到 AIDL 類後,就能夠調用 AIDL 類中定義好的操做,進行跨進程請求

@OnClick(R.id.btn_add_person)
public void addPerson() {
    Random random = new Random();
    Person person = new Person("shixin" + random.nextInt(10));

    try {
        mAidl.addPerson(person);
        List<Person> personList = mAidl.getPersonList();
        mTvResult.setText(personList.toString());
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
運行結果


能夠看到,Activity 與 另一個進程的 Service 通訊成功了。

總結
這篇文章介紹了 AIDL 的簡單編寫流程,其中也踩過一些坑,好比文件所在包的路徑不統一,綁定服務收不到回調等問題。

到最後雖然跨進程通訊成功,可是咱們仍是有不少疑問的,好比:

AIDL 生成的文件內容?
什麼是 Binder?
爲何要這麼寫?
知其然還要知其因此然,這一切都要從 Binder 講起,且聽下一回合介紹。

代碼地址 Thanks 《Android 開發藝術探索》  https://developer.android.com/guide/components/aidl.html  http://www.jianshu.com/p/b9b15252b3d6  http://rainbow702.iteye.com/blog/1149790 ---------------------  做者:拭心  來源:CSDN  原文:https://blog.csdn.net/u011240877/article/details/72765136  版權聲明:本文爲博主原創文章,轉載請附上博文連接!

相關文章
相關標籤/搜索