一.相關(guān)介紹
在 Android 系統(tǒng)中,進(jìn)程間通信 (IPC) 是一種很重要的機(jī)制。IPC 產(chǎn)生的原因是某些情況下需要在兩個(gè)進(jìn)程之間進(jìn)行一些數(shù)據(jù)的交換。而在深入學(xué)習(xí) Android 的過程中難免會(huì)遇到 IPC 的相關(guān)問題,比如常見的有在自己的應(yīng)用程序中讀取手機(jī)聯(lián)系人的信息,這就涉及到 IPC 了。因?yàn)樽约旱膽?yīng)用程序是一個(gè)進(jìn)程,通訊錄也是一個(gè)進(jìn)程,只不過獲取通訊錄的數(shù)據(jù)信息是通過 Content Provider 的方式來實(shí)現(xiàn)的。
對(duì)于初學(xué)者來說,在一開始接觸 IPC 時(shí)可能會(huì)摸不著頭腦,因?yàn)榫W(wǎng)上很多博客在講 Android IPC 時(shí)通常都是長(zhǎng)篇大論,沒有從例子著手?;谝陨戏N種原因以及希望對(duì) AIDL 有一個(gè)更深入的理解,本篇博文就誕生了。在 Android 系統(tǒng)中,IPC 的方式有很多種,比如有 Messenger 、AIDL 和 ContentProvider 等。我們今天就來講講其中的 AIDL ,AIDL 也是比較常見和經(jīng)常使用的一種 IPC 方式。希望讀者在看完本篇之后對(duì)于 AIDL 有一個(gè)比較深入的理解。
什么是 AIDL?
AIDL 的全稱是 Android Interface Definition Language(即 Android 接口定義語言)。
AIDL是Binder的實(shí)例。
AIDL的使用實(shí)例:
我們來模擬一下需要進(jìn)行 IPC 的情況,現(xiàn)在有客戶端和服務(wù)端,客戶端通過 AIDL 來和服務(wù)端進(jìn)行 IPC 。我們假定現(xiàn)在客戶端需要傳一個(gè) Person 類的對(duì)象給服務(wù)端,之后服務(wù)端回傳給客戶端一個(gè) Person 類的集合。
1.服務(wù)端的相關(guān)代碼
以下 Person.aidl 文件:
parcelable Person;
注意在 IPC 機(jī)制中傳遞的自定義對(duì)象需要序列化,所以要實(shí)現(xiàn) Parcelable 接口。在 AIDL 文件中使用parcelable關(guān)鍵字聲明。有了 Person.aidl 之后,我們就要?jiǎng)?chuàng)建 AIDL 接口了。
interface AddPersonInter {
List<Person> addPerson(in Person person);
}
在 IMyAidlInterface.aidl 里,主要聲明一個(gè)用于添加 Person 對(duì)象的抽象方法。另外,需要注意以下幾點(diǎn):
1.Person 類需要手動(dòng)去 import ,在 AIDL 文件中不能自動(dòng)導(dǎo)包;
2.在addPerson方法里需要聲明參數(shù)是 in 的,用來表示該參數(shù)是傳入的。除了 in 之外,還有 out 和 inout ;
下面我們要?jiǎng)?chuàng)建一個(gè) Service 用于和客戶端進(jìn)行 IPC 。這里還要把該 Service 運(yùn)行在一個(gè)新的進(jìn)程里。我們只要在 AndroidManifest.xml 中聲明android:process=":remote"就行了。
public class MyService extends Service {
private static final String TAG = "MyService";
private List<Person> persons = new ArrayList<>();
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
Log.e("aaaa", "binder綁定成功");
return binder;
}
private final IBinder binder = new AddPersonInter.Stub() {
@Override
public List<Person> addPerson(Person person) throws RemoteException {
synchronized (persons) {
persons.add(person);
Log.e("aaaaa", "服務(wù)端 name----" + person.getName() + " age===" + person.getAge());
return persons;
}
}
};
}
在上面的代碼中我們可以看到在onBind(Intent intent)方法中返回了 mBinder ,而客戶端正是通過這個(gè) mBinder 來和服務(wù)端進(jìn)行 IPC 的。mBinder 是 IMyAidlInterface.Stub 匿名類的對(duì)象,IMyAidlInterface.Stub 其實(shí)是一個(gè)抽象類,繼承自 Binder ,實(shí)現(xiàn)addPerson方法。這里要注意以下,在addPerson的方法中需要將 persons 同步,這是因?yàn)樵诜?wù)端 AIDL 是運(yùn)行在 Binder 線程池中的,有可能會(huì)有多個(gè)客戶端同時(shí)連接,這時(shí)候就需要同步以防止數(shù)據(jù)出錯(cuò)。
2.客戶端的相關(guān)代碼
客戶端需要將服務(wù)端的aidl文件夾整體復(fù)制到客戶端,并將用到到的java類Person.java復(fù)制到客戶端,
注意包名一致,不然編譯會(huì)報(bào)錯(cuò)。
public class Main2Activity extends AppCompatActivity {
TextView tv;
private AddPersonInter addPersonInter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv=this.findViewById(R.id.tv);
// 啟動(dòng)服務(wù)端的服務(wù),并進(jìn)行綁定
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.seventeenok.test", "com.seventeenok.test.MyService"));
bindService(intent, conn, Context.BIND_AUTO_CREATE);
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
try {
List<Person> list = addPersonInter.addPerson(new Person("lizhi", 24));
} catch (RemoteException e) {
e.printStackTrace();
}
}
}).start();
}
});
}
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
addPersonInter = AddPersonInter.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
addPersonInter = null;
}
};
}
AIDL 的流程基本上就是這樣的。通過這個(gè)簡(jiǎn)單的例子,相信對(duì)于 AIDL 有了一個(gè)初步的了解。下面我們就要進(jìn)行 AIDL 的代碼分析。
3.項(xiàng)目的整體目錄結(jié)構(gòu):

AIDL代碼分析
工程中的 gen 目錄下找到對(duì)應(yīng) AIDL 編譯后的文件:
public interface AddPersonInter extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.seventeenok.test.AddPersonInter {
private static final java.lang.String DESCRIPTOR = "com.seventeenok.test.AddPersonInter";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.seventeenok.test.AddPersonInter interface,
* generating a proxy if needed.
*/
public static com.seventeenok.test.AddPersonInter asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.seventeenok.test.AddPersonInter))) {
return ((com.seventeenok.test.AddPersonInter) iin);
}
return new com.seventeenok.test.AddPersonInter.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_addPerson: {
data.enforceInterface(DESCRIPTOR);
com.seventeenok.test.Person _arg0;
if ((0 != data.readInt())) {
_arg0 = com.seventeenok.test.Person.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
java.util.List<com.seventeenok.test.Person> _result = this.addPerson(_arg0);
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.seventeenok.test.AddPersonInter {
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.seventeenok.test.Person> addPerson(com.seventeenok.test.Person person) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.seventeenok.test.Person> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((person != null)) {
_data.writeInt(1);
person.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.seventeenok.test.Person.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public java.util.List<com.seventeenok.test.Person> addPerson(com.seventeenok.test.Person person) throws android.os.RemoteException;
}
可以看到編譯后的 AddPersonInter.aidl 變成了一個(gè)接口,繼承自 IInterface 。在 AddPersonInter接口中我們發(fā)現(xiàn)主要分成兩部分結(jié)構(gòu):抽象類 Stub 和原來 aidl 中聲明的addPerson方法。
重點(diǎn)在于 Stub 類,下面我們來分析一下。從 Stub 類中我們可以看到是繼承自 Binder 并且實(shí)現(xiàn)了 AddPersonInter接口。 Stub 類的基本結(jié)構(gòu)如下:
asInterface(android.os.IBinder obj)方法;
asBinder()方法;
onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)方法;
靜態(tài)類Proxy,主要方法是addPerson(com.seventeenok.test.Person person);
靜態(tài)常量TRANSACTION_addPerson;
asInterface(android.os.IBinder obj)
我們先從asInterface(android.os.IBinder obj)方法入手,在上面的代碼中可以看到,主要的作用就是根據(jù)傳入的Binder對(duì)象轉(zhuǎn)換成客戶端需要的 AddPersonInter接口。如果服務(wù)端和客戶端處于同一個(gè)進(jìn)程,那么該方法得到的就是服務(wù)端 Stub 對(duì)象本身,也就是上面 AIDL 例子 MyService 中的 mBinder 對(duì)象;否則返回的是系統(tǒng)封裝后的 Stub.Proxy ,也就是一個(gè)代理類,在這個(gè)代理中實(shí)現(xiàn)跨進(jìn)程通信。
asBinder()
該方法就是返回當(dāng)前的 Binder 對(duì)象。
onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
在onTransact方法中,根據(jù)傳入的 code 值會(huì)去執(zhí)行服務(wù)端相對(duì)應(yīng)的方法。其中靜態(tài)變量TRANSACTION_addPerson就是其中的 code 值之一(在 AIDL 文件中聲明的方法有多少個(gè)就有多少個(gè)對(duì)應(yīng)的 code )。其中 data 就是服務(wù)端方法中所需要的參數(shù),執(zhí)行完后,最后把方法的返回結(jié)果放入 reply 中傳遞給客戶端。若該方法返回 false ,那么客戶端請(qǐng)求失敗。
Proxy中的addPerson(com.seventeenok.test.Person person)
Proxy 類是實(shí)現(xiàn)了 AddPersonInter接口,把其中的addPerson方法進(jìn)行了重寫。在方法中一開始創(chuàng)建了兩個(gè) Parcel 對(duì)象,其中一個(gè)用來把方法的參數(shù)裝入,然后調(diào)用transact方法執(zhí)行服務(wù)端的代碼,執(zhí)行完后把返回的結(jié)果裝入另外一個(gè) Parcel 對(duì)象中返回。
看完上面方法的介紹,我們回過頭來看看 AIDL 例子中實(shí)現(xiàn)的流程。在客戶端中通過 Intent 去綁定一個(gè)服務(wù)端的 Service 。在onServiceConnected(ComponentName name, IBinder service)方法中通過返回的 service 可以得到對(duì)應(yīng)的 AIDL 接口的實(shí)例。這是調(diào)用了asInterface(android.os.IBinder obj)方法來完成的。
客戶端在onServiceConnected(ComponentName name, IBinder service)中得到的 service 正是服務(wù)端中的 mBinder 。當(dāng)客戶端調(diào)用 AIDL 接口時(shí),AIDL 通過 Proxy 類中的addPerson來調(diào)用transact方法,transact方法又會(huì)去調(diào)用服務(wù)端的onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)方法。onTransact方法是運(yùn)行在服務(wù)端的 Binder 線程池中的。在onTransact中根據(jù) code 執(zhí)行相關(guān) AIDL 接口的方法,方法的參數(shù)從 data 中獲取。執(zhí)行完畢之后把結(jié)果裝入 reply 中返回給客戶端。 AIDL 的流程基本上就是這樣子了。
總結(jié)
AIDL 在 Android IPC 機(jī)制中算得上是很重要的一部分,AIDL 主要是通過 Binder 來實(shí)現(xiàn)進(jìn)程通信的。當(dāng)然,上面只是簡(jiǎn)單的例子分析AIDL的整個(gè)流程,并沒有涉及到死亡代理、權(quán)限驗(yàn)證等功能,有想法的同學(xué)可以深入研究下。