一、前言
在 Framework 源碼解析知識梳理(1) - 應用進程與 AMS 的通信實現(xiàn) 和 Framework 源碼解析知識梳理(2) - 應用進程與 WMS 的通信實現(xiàn) 這兩篇文章中,我們介紹了應用進程與AMS以及WMS之間的通信實現(xiàn),但是邏輯還是比較繞的,為了方便大家更好地理解,我們介紹一下大家見得比較多的應用進程間通信的實現(xiàn)。
二、例子
說起應用進程之間的通信,相信大家都不陌生,應用進程之間通信最常用的方式就是AIDL,下面,我們先演示一個AIDL的簡單例子,接下來,我們再分析它的內(nèi)部實現(xiàn)。
2.1 服務端
2.1.1 編寫 AIDL 文件
第一件事,就是服務端需要聲明自己可以為客戶端提供什么功能,而這一聲明則需要通過一個.aidl文件來實現(xiàn),我們先簡要介紹介紹一下AIDL文件:
-
AIDL文件的后綴為*.aidl - 對于
AIDL默認支持的數(shù)據(jù)類型,是不需要導入包的,這些默認的數(shù)據(jù)類型包括: - 基本數(shù)據(jù)類型:
byte/short/int/long/float/double/boolean/char String/CharSequence-
List<T>:其中T必須是AIDL支持的類型,或者是其它AIDL生成的接口,或者是實現(xiàn)了Parcelable接口的對象,List支持泛型 -
Map:它的要求和List類似,但是不支持泛型 -
Tag標簽:對于接口方法中的形參,我們需要用in/out/inout三種關鍵詞去修飾: -
in:表示服務端會收到客戶端的完整對象,但是在服務端對這個對象的修改不會同步給客戶端 -
out:表示服務端會收到客戶端的空對象,它對這個對象的修改會同步到客戶端 -
inout:表示服務端會收到客戶端的完整對象,它對這個對象的修改會同步到客戶端
說了這么多,我們最常見的需求無非就是兩個:讓復雜對象實現(xiàn)Parcelable接口實現(xiàn)傳輸以及定義接口方法。
(1) Parcelable 的實現(xiàn)
我們可以很方便地通過AS插件來讓某個對象實現(xiàn)Parcelable接口,并自動補全要實現(xiàn)的方法:

安裝完之后重啟,我們原本的對象為:
public class InObject {
private int inData;
public int getInData() {
return inData;
}
public void setInData(int inData) {
this.inData = inData;
}
}
在文件的空白處,點擊右鍵Generate -> Parcelable:

它就會幫我們實現(xiàn)
Parcelable中的接口:
public class InObject implements Parcelable {
private int inData;
public int getInData() {
return inData;
}
public void setInData(int inData) {
this.inData = inData;
}
public InObject() {}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.inData);
}
protected InObject(Parcel in) {
this.inData = in.readInt();
}
public static final Parcelable.Creator<InObject> CREATOR = new Parcelable.Creator<InObject>() {
@Override
public InObject createFromParcel(Parcel source) {
return new InObject(source);
}
@Override
public InObject[] newArray(int size) {
return new InObject[size];
}
};
}
(2) 編寫 AIDL 文件
點擊File -> New -> AIDL -> AIDL File之后,會多出一個名叫aidl的文件夾,之后我們所有需要用到的aidl文件都被存放在這里:

初始時候,
AS為我們生成的AIDL文件為:
// AIDLInterface.aidl
package com.demo.lizejun.binderdemoclient;
// Declare any non-default types here with import statements
interface AIDLInterface {
/**
* 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);
}
當我們編譯之后,就會在下面這個路徑中生成一個Java文件:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /home/lizejun/Repository/RepoGithub/BinderDemo/app/src/main/aidl/com/demo/lizejun/binderdemoclient/AIDLInterface.aidl
*/
package com.demo.lizejun.binderdemoclient;
// Declare any non-default types here with import statements
public interface AIDLInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.demo.lizejun.binderdemoclient.AIDLInterface {
private static final java.lang.String DESCRIPTOR = "com.demo.lizejun.binderdemoclient.AIDLInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.demo.lizejun.binderdemoclient.AIDLInterface interface,
* generating a proxy if needed.
*/
public static com.demo.lizejun.binderdemoclient.AIDLInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.demo.lizejun.binderdemoclient.AIDLInterface))) {
return ((com.demo.lizejun.binderdemoclient.AIDLInterface) iin);
}
return new com.demo.lizejun.binderdemoclient.AIDLInterface.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_basicTypes: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0 != data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.demo.lizejun.binderdemoclient.AIDLInterface {
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;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean) ? (1) : (0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
}
整個結構圖為:

有沒有感覺在 Framework 源碼解析知識梳理(1) - 應用進程與 AMS 的通信實現(xiàn) 和 Framework 源碼解析知識梳理(2) - 應用進程與 WMS 的通信實現(xiàn) 中也見到過類似的東西
asInterface/asBinder/transact/onTransact/Stub/Proxy...,這個我們之后再來解釋,下面我們介紹服務端的第二步操作。
2.1.2 編寫 Service
既然服務端已經(jīng)定義好了接口,那么接下來就服務端就需要實現(xiàn)這些接口:
public class AIDLService extends Service {
private final AIDLInterface.Stub mBinder = new AIDLInterface.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
Log.d("basicTypes", "basicTypes");
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
在這個Service中,我們只需要實現(xiàn)AIDLInterface.Stub接口中的basicTypes接口就可以了,它就是我們在AIDL文件中定義的接口,再把這個對象通過onBinde方法返回。
2.1.3 聲明 Service
最后一步,在AndroidManifest.xml文件中聲明這個Service:
<service
android:name=".server.AIDLService"
android:enabled="true"
android:exported="true">
</service>
2.2 客戶端
2.2.1 編寫 AIDL 文件
和服務端類似,客戶端也需要和服務端一樣,生成一個相同的AIDL文件,這里就不多說了:
// AIDLInterface.aidl
package com.demo.lizejun.binderdemoclient;
// Declare any non-default types here with import statements
interface AIDLInterface {
/**
* 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生成的Java接口文件。
2.2.2 綁定服務,并調(diào)用
(1) 綁定服務
private void bind() {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.demo.lizejun.binderdemo", "com.demo.lizejun.binderdemo.server.AIDLService"));
boolean result = bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
Log.d("bind", "result=" + result);
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBinder = AIDLInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mBinder = null;
}
};
(2) 調(diào)用接口
public void sayHello(View view) {
try {
if (mBinder != null) {
mBinder.basicTypes(0, 0, false, 0, 0, "aaa");
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
之后我們打印出響應的log:

三、實現(xiàn)原理
下面,我們就一起來分析一下通過AIDL實現(xiàn)的進程間通信的原理。
**(1) 客戶端獲得服務端的遠程代理對象 IBinder **
我們從客戶端說起,當我們在客戶端調(diào)用了bindService方法之后,就會啟動服務端實現(xiàn)的AIDLService,而在該AIDLService的onBind()方法中返回了AIDLInterface.Stub的實現(xiàn)類,綁定成功之后,客戶端就通過onServiceConnected的回調(diào),得到了它在客戶端進程的遠程代理對象IBinder。
(2) AIDLInterface.Stub.asInterface(IBinder)
在拿到這個IBinder對象之后,我們通過AIDLInterface.Stub.asInterface(IBinder)方法,對這個IBinder進行了一層包裝,轉換成為AIDLInterface接口類,那么這個AIDLInterface是怎么來的呢,它就是通過我們在客戶端定義的aidl文件在編譯時生成的,可以看到,最終asInterface方法會返回給我們一個AIDLInterface的實現(xiàn)類Proxy,IBinder則被保存為它內(nèi)部的一個成員變量mRemote。

也就是說,我們在客戶端中保存的
mBinder實際上是一個由AIDL文件所生成的Proxy對象。
(3) 調(diào)用 AIDLInterface 的接口方法
Proxy實現(xiàn)了AIDLInterface中定義的接口方法,當我們調(diào)用它的接口方法時:

實際上是通過它內(nèi)部的
mRemote對象的transact方法發(fā)送消息:
(4) 服務端接收消息
那么客戶端發(fā)送的這一消息去哪里了呢,回想一下在服務端中通過onBind()放回的IBinder,它其實是由AIDL文件生成的AIDLInterface.java中的內(nèi)部類Stub的實現(xiàn),而在Stub中有一個回調(diào)函數(shù)onTransact,當我們通過客戶端發(fā)送消息之后,那么服務端的Stub對象就會通過onTransact收到這一發(fā)送的消息:

(5) 調(diào)用子類的處理邏輯
在onTransact方法中,又會去調(diào)用AIDLInterface定義的接口:

而我們在服務端AIDLService中實現(xiàn)了該接口,因此,最終就打印出了我們在上面所看到的文字:

四、進一步討論
現(xiàn)在,我們通過這一進程間的通信過程來復習一下前面兩篇文章中討論的應用進程與AMS和WMS之間的通信實現(xiàn)。
在 Framework 源碼解析知識梳理(1) - 應用進程與 AMS 的通信實現(xiàn) 中,AMS所在進程在收到消息之后,進行了下面的處理邏輯:

這里面的處理邏輯就相當于我們在客戶端中
onServiceConnected中做的工作一樣,IApplicationThread實際上是一個ApplicaionThreadProxy對象,和我們上面AIDLInterface實際上是一個AIDLInterface.Stub.Proxy的原理是一樣的。當我們調(diào)用了它的接口方法時,就是通過內(nèi)部的mRemote發(fā)送了消息。
在AMS通信中,接收消息的是應用進程中的ApplicationThread,而我們上面例子中接收消息的是服務端進程的AIDLInterface.Stub的實現(xiàn)類mBinder,同樣是在onTransact()方法中接收消息,再由子類去實現(xiàn)處理的邏輯。
Framework 源碼解析知識梳理(2) - 應用進程與 WMS 的通信實現(xiàn) 中的原理就更好解釋了,因為它就是通過AIDL來實現(xiàn)的,我們從客戶端向WMS所在進程建立會話時,是通過IWindowSession來實現(xiàn)的,會話過程中IWindowSession就對應于AIDLInterface,而WMS中的IWindowSession.Stub的實現(xiàn)類Session,就對應于上面我們在AIDLService中定義的AIDLInterface.Stub的實現(xiàn)類mBinder。
五、小結
其實AIDL并沒有什么神秘的東西,它的本質就是Binder通信,我們定義aidl文件的目的,主要有兩個:
- 為了讓應用進程間通信的實現(xiàn)者不用再去編寫
transact/onTransact里面的代碼,因為這些東西和業(yè)務邏輯是無關的,只不過是簡單的發(fā)送消息、接收消息。 - 讓服務端、客戶端只關心接口的定義。
如果我們明白了AIDL的原理,那么我們完全可以不用定義AIDL文件,自己去參考由AIDL文件所生成的Java文件的邏輯,進行消息的發(fā)送和處理。
更多文章,歡迎訪問我的 Android 知識梳理系列:
- Android 知識梳理目錄:http://www.itdecent.cn/p/fd82d18994ce
- 個人主頁:http://lizejun.cn
- 個人知識總結目錄:http://lizejun.cn/categories/