讀完本文你將瞭解: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 版權聲明:本文爲博主原創文章,轉載請附上博文連接!