在 Framework 源碼解析知識梳理(1) - 應用進程與 AMS 的通訊實現 和 Framework 源碼解析知識梳理(2) - 應用進程與 WMS 的通訊實現 這兩篇文章中,咱們介紹了應用進程與AMS
以及WMS
之間的通訊實現,可是邏輯仍是比較繞的,爲了方便你們更好地理解,咱們介紹一下你們見得比較多的應用進程間通訊的實現。java
提及應用進程之間的通訊,相信你們都不陌生,應用進程之間通訊最經常使用的方式就是AIDL
,下面,咱們先演示一個AIDL
的簡單例子,接下來,咱們再分析它的內部實現。android
第一件事,就是服務端須要聲明本身能夠爲客戶端提供什麼功能,而這一聲明則須要經過一個.aidl
文件來實現,咱們先簡要介紹介紹一下AIDL
文件:bash
AIDL
文件的後綴爲*.aidl
AIDL
默認支持的數據類型,是不須要導入包的,這些默認的數據類型包括:byte/short/int/long/float/double/boolean/char
String/CharSequence
List<T>
:其中T
必須是AIDL
支持的類型,或者是其它AIDL
生成的接口,或者是實現了Parcelable
接口的對象,List
支持泛型Map
:它的要求和List
相似,可是不支持泛型Tag
標籤:對於接口方法中的形參,咱們須要用in/out/inout
三種關鍵詞去修飾:in
:表示服務端會收到客戶端的完整對象,可是在服務端對這個對象的修改不會同步給客戶端out
:表示服務端會收到客戶端的空對象,它對這個對象的修改會同步到客戶端inout
:表示服務端會收到客戶端的完整對象,它對這個對象的修改會同步到客戶端說了這麼多,咱們最多見的需求無非就是兩個:讓複雜對象實現Parcelable
接口實現傳輸以及定義接口方法。app
(1) Parcelable 的實現ide
咱們能夠很方便地經過AS
插件來讓某個對象實現Parcelable
接口,並自動補全要實現的方法: 函數
public class InObject {
private int inData;
public int getInData() {
return inData;
}
public void setInData(int inData) {
this.inData = inData;
}
}
複製代碼
在文件的空白處,點擊右鍵Generate -> Parcelable
: ui
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 文件this
點擊File -> New -> AIDL -> AIDL File
以後,會多出一個名叫aidl
的文件夾,以後咱們全部須要用到的aidl
文件都被存放在這裏: spa
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;
}
複製代碼
整個結構圖爲:
asInterface/asBinder/transact/onTransact/Stub/Proxy...
,這個咱們以後再來解釋,下面咱們介紹服務端的第二步操做。
既然服務端已經定義好了接口,那麼接下來就服務端就須要實現這些接口:
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
中,咱們只須要實現AIDLInterface.Stub
接口中的basicTypes
接口就能夠了,它就是咱們在AIDL
文件中定義的接口,再把這個對象經過onBinde
方法返回。
最後一步,在AndroidManifest.xml
文件中聲明這個Service
:
<service
android:name=".server.AIDLService"
android:enabled="true"
android:exported="true">
</service>
複製代碼
和服務端相似,客戶端也須要和服務端同樣,生成一個相同的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
接口文件。
(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) 調用接口
public void sayHello(View view) {
try {
if (mBinder != null) {
mBinder.basicTypes(0, 0, false, 0, 0, "aaa");
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
複製代碼
以後咱們打印出響應的log
:
下面,咱們就一塊兒來分析一下經過AIDL
實現的進程間通訊的原理。
**(1) 客戶端得到服務端的遠程代理對象 IBinder **
咱們從客戶端提及,當咱們在客戶端調用了bindService
方法以後,就會啓動服務端實現的AIDLService
,而在該AIDLService
的onBind()
方法中返回了AIDLInterface.Stub
的實現類,綁定成功以後,客戶端就經過onServiceConnected
的回調,獲得了它在客戶端進程的遠程代理對象IBinder
。
(2) AIDLInterface.Stub.asInterface(IBinder)
在拿到這個IBinder
對象以後,咱們經過AIDLInterface.Stub.asInterface(IBinder)
方法,對這個IBinder
進行了一層包裝,轉換成爲AIDLInterface
接口類,那麼這個AIDLInterface
是怎麼來的呢,它就是經過咱們在客戶端定義的aidl
文件在編譯時生成的,能夠看到,最終asInterface
方法會返回給咱們一個AIDLInterface
的實現類Proxy
,IBinder
則被保存爲它內部的一個成員變量mRemote
。
mBinder
其實是一個由
AIDL
文件所生成的
Proxy
對象。
(3) 調用 AIDLInterface 的接口方法
Proxy
實現了AIDLInterface
中定義的接口方法,當咱們調用它的接口方法時:
mRemote
對象的
transact
方法發送消息:
(4) 服務端接收消息
那麼客戶端發送的這一消息去哪裏了呢,回想一下在服務端中經過onBind()
放回的IBinder
,它實際上是由AIDL
文件生成的AIDLInterface.java
中的內部類Stub
的實現,而在Stub
中有一個回調函數onTransact
,當咱們經過客戶端發送消息以後,那麼服務端的Stub
對象就會經過onTransact
收到這一發送的消息:
(5) 調用子類的處理邏輯
在onTransact
方法中,又會去調用AIDLInterface
定義的接口:
而咱們在服務端AIDLService
中實現了該接口,所以,最終就打印出了咱們在上面所看到的文字:
如今,咱們經過這一進程間的通訊過程來複習一下前面兩篇文章中討論的應用進程與AMS
和WMS
之間的通訊實現。
在 Framework 源碼解析知識梳理(1) - 應用進程與 AMS 的通訊實現 中,AMS
所在進程在收到消息以後,進行了下面的處理邏輯:
onServiceConnected
中作的工做同樣,
IApplicationThread
其實是一個
ApplicaionThreadProxy
對象,和咱們上面
AIDLInterface
其實是一個
AIDLInterface.Stub.Proxy
的原理是同樣的。當咱們調用了它的接口方法時,就是經過內部的
mRemote
發送了消息。
在AMS
通訊中,接收消息的是應用進程中的ApplicationThread
,而咱們上面例子中接收消息的是服務端進程的AIDLInterface.Stub
的實現類mBinder
,一樣是在onTransact()
方法中接收消息,再由子類去實現處理的邏輯。
Framework 源碼解析知識梳理(2) - 應用進程與 WMS 的通訊實現 中的原理就更好解釋了,由於它就是經過AIDL
來實現的,咱們從客戶端向WMS
所在進程創建會話時,是經過IWindowSession
來實現的,會話過程當中IWindowSession
就對應於AIDLInterface
,而WMS
中的IWindowSession.Stub
的實現類Session
,就對應於上面咱們在AIDLService
中定義的AIDLInterface.Stub
的實現類mBinder
。
其實AIDL
並無什麼神祕的東西,它的本質就是Binder
通訊,咱們定義aidl
文件的目的,主要有兩個:
transact/onTransact
裏面的代碼,由於這些東西和業務邏輯是無關的,只不過是簡單的發送消息、接收消息。若是咱們明白了AIDL
的原理,那麼咱們徹底能夠不用定義AIDL
文件,本身去參考由AIDL
文件所生成的Java
文件的邏輯,進行消息的發送和處理。