AIDL它是Android衆多進程間通訊方式中的一種,底層是Binder機制的實現,因此想要讀懂AIDL自動生成的代碼中各個類的做用,就必須對Binder有必定的瞭解,其實不止AIDL,Android進程間通訊的大多數方式的底層都是Binder機制,本文主要介紹AIDL的使用,因此不會介紹Binder機制的原理,關於Binder機制的原理,我推薦下面的一篇文章,零基礎也能看懂:html
寫給 Android 應用工程師的 Binder 原理剖析java
看完上面的文章你也就能對Binder原理的實現有大概的瞭解,對於咱們Android應用開發也就足夠了,若是你還不知足,想從底層和源碼瞭解Binder機制,你能夠閱讀下面的兩個連接:android
Binder系列—開篇github
上面兩個系列就是從設計和源碼的角度去解讀Binder,有點深刻。好了,對Binder有一個大致上的認識後,接下來咱們就要經過AIDL的使用來完成Android進程間通訊的實踐。數組
在介紹AIDL以前,咱們先來看一下Android中除了AIDL還有哪些進程間通訊的方式:網絡
一、Bundle架構
Bundle實現了Parcelable,因此在Android中咱們能夠經過Intent在不一樣進程間傳遞Bundle數據。併發
可是在Intent 傳輸數據的過程當中,用到了 Binder,Intent中的數據,即Bundle數據,會做爲 Parcel 存儲在Binder 的事務緩衝區(Binder transaction buffer)中的對象進行傳輸,而這個 Binder 事務緩衝區具備一個有限的固定大小,約爲1MB,並且這個1Mb並非當前進程所獨享,而是全部進程共享的,因此因爲1Mb的限制,Bundle不能存放大量的數據,否則會報TransactionTooLargeException,而且Bundle中存放的數據也要求可以被序列化,因此Bundle只適用於數據量小和簡單的進程間通訊。app
二、文件共享
兩個進程間經過讀寫同一個文件來交換數據。
可是因爲Android容許併發的對同一個文件讀寫,因此若是兩個進程同時的對這個文件寫,就會出現問題,因此這適用於對數據同步要求不高的進程間通訊。
三、ContentProvider
ContentProvider是Android中專門用於不一樣應用之間,即不一樣進程之間進行數據共享的方式,它的底層實現是Binder。
ContentProvider是四大組件之一,它的使用比較簡單,咱們只須要繼承自ContenProvider,並重寫它的六個方法:onCreate、query、updata、insert、delete和getType,其中onCreate用於初始化,getType用於返回一個Uri請求表明的MIME類型,剩下的都是CRUD操做,除了onCreate方法運行在主線程,其餘方法都運行在Binder線程池,而後在另一個進程中註冊這個ContentProvider,在本進程的Activity中經過getContentResolver()得到ContentResolver後,再經過ContentResolver來完成對這個ContentProvider的CRUD操做。
四、套接字(Socket)
一種傳統的Linux IPC方式,除了用於不一樣進程之間還能夠用於不一樣機器之間(經過網絡傳輸)的通訊,可是它的傳輸效率也是很是的低。
五、Messenger
經過Messenger能夠在不一樣進程之間傳遞Message對象,Message中能夠放入咱們須要傳遞的數據,它的底層實現是AIDL。
可是Messenger在服務端的Handler是以串行的方式處理來自客戶端的Message,因此若是有大量的併發請求,Messenger就效率低下,因此Messenger適用於數據併發量低的進程間通訊。
六、AIDL
也就是本文的主角,它的底層實現是Binder。
能夠看到Android中進程間通訊的方式中,除了文件共享和Socket,底層都是須要經過Binder來傳輸數據,可見Binder對Android的進程間通訊來講是多麼的重要。
Android中要在進程間傳輸的數據都要是可序列化的,序列化就是把對象轉換成二進制(字節流),從而能夠存儲到存儲設備或者經過網絡進行傳輸,有序列化,就有反序列,反序列化就是把二進制(字節流)轉化爲對象,其中8種基本數據類型默承認以在進程間傳輸,沒有序列化的概念,序列化的做用對象是對象。在Android中,對象經過實現Serializable或Parcelable接口來實現序列化,下面簡單介紹一下它們之間使用和區別:
Serializable是java中提供的一個序列化接口,它是一個空接口,對象實現這個Serializable接口,就標記這個對象是可序列化的,而後咱們經過ObjectOutputStream的writeObject方法就能完成對象的序列化,以下:
User user = new User();
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("D://test.txt"));
os.writeObject(user);
os.close();
複製代碼
同理,經過ObjectOutputStream的readObject方法就能完成對象的反序列化,以下:
ObjectOutputStream in = new ObjectInputStream(new FileInputStream("D://test.txt"));
User user = in.readObject();
in.close();
複製代碼
若是你在序列化這個對象以後有可能會改變這個對象的類結構,例如爲類添加新的字段,這時在序列化這個對象以前你就要爲這個對象的類加上一個serialVersionUID參數,以下:
public class User{
//serialVersionUID取什麼值不要緊,只要保持不變就行
private static final long serialVersionUID = 1L;
}
複製代碼
若是你確保這個對象的類結構在序列化以後是一直不變的,那麼這個serialVersionUID能夠忽略,爲何會這樣呢?這是由於在序列化的時候,系統會把當前類的serialVersionUID寫入到序列化的文件中,當反序列化時系統就會去檢測文件中的serialVersionUID是否和當前類的serialVersionUID相同,若是相同就說明序列化的類和當前類的版本是相同的,這時候系統能夠正確的反序列化,若是不一致,就說明序列化的類和當前類相比發生了某些變化,例如當前類添加了新的字段,這時就會反序列化失敗,拋出異常。因此若是你手動指定了serialVersionUID的值,並一直保持不變,就算你改變了類的結構(不要改變類名和變量類型),序列化和反序列化時二者的serialVersionUID都是相同的,能夠正常的進行反序列化,相反,若是你沒有指定serialVersionUID的值,系統會根據類的結構自動生成它的hash值做爲serialVersionUID的值,這時你改變了類的結構,在進行序列化和反序列化時二者生成的serialVersionUID不相同,這時反序列化就會失敗,因此當你沒有指定serialVersionUID的值,你要確保這個對象的類結構在序列化以後是一直不變。固然大多數狀況下咱們仍是會指定serialVersionUID的值,以防咱們不當心修改類的結構致使沒法恢復對象。
這裏只是列舉了用Serializable實現對象序列化最簡單的使用,其實咱們能夠經過transient關鍵字控制哪些字段不被序列化,還有靜態成員不屬於對象,不會參與序列化過程,還能夠手動控制Serializable對象的序列化和反序列化過程,還關於Serializable更多使用方式能夠看Java對象表示方式1:序列化、反序列化和transient關鍵字的做用。
Parcelable是Android提供的一個接口,一個對象只要實現了這個接口,就能夠實現序列化,以下:
public class User implements Parcelable {
private final String name;
private final String password;
public User(String name, String password) {
this.name = name;
this.password = password;
}
public String getName() {
return name == null ? "" : name;
}
public String getPassword() {
return password == null ? "" : password;
}
//下面就是實現Parcelable接口須要實現的方法
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
dest.writeString(this.password);
}
public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() {
@Override
public User createFromParcel(Parcel source) {
return new User(source);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
protected User(Parcel in) {
this.name = in.readString();
this.password = in.readString();
}
}
複製代碼
User中須要序列化的字段都會包裝進Parcel中,當反序列化時,從Parcel中讀取數據,Parcel內部包裝了可序列化的數據,能夠在進程間傳輸,其中describeContents方法用來完成內容描述,通常返回0,writeToParcel方法用來實現序列化,CREATOR對象的內部方法用來實現反序列化,其中createFromParcel方法用來從序列化後的對象中建立原始對象,newArray方法用來建立指定長度的原始對象數組。
Parcelable的用法相比Serializable複雜了一點,若是每次須要序列化一個對象,都要寫這麼多重複樣本代碼,會有點累,這裏我推薦一個插件android-parcelable-intellij-plugin,專門用於自動生成這些重複樣本代碼。
Serializable和Parcelable均可以實現序列化,用於進程間的數據傳遞,它們之間有什麼區別呢?在編碼上,Serializable使用簡單,而Parcelable稍顯複雜;在效率上,Serializable的開銷很大,由於它只是一個空接口,咱們無需實現任何方法,Java便會對這個對象進行序列化操做,這是由於java會經過反射建立所需方法,致使序列化的過程較慢,而Parcelable的效率高,它的速度比Serializable快十倍,經過把一個完整的對象進行分解,而分解後的每一部分都是Intent所支持的數據類型,這樣也就實現了傳遞對象的功能;在使用場景上,Parcelable主要用在內存序列化上,它不能使用在要將數據序列化在磁盤上的狀況,由於在外界有變化的狀況下,Parcelable不能很好的保證數據的持續性,因此若是須要序列化到磁盤或經過網絡傳輸建議使用Serializable,儘管它的效率低點。
綜上所述,在Android開發中,若是有序列化的要求,咱們多點考慮使用Parcelable,畢竟它是Android自帶的,雖然它使用稍顯複雜,可是有插件的幫助,加上它的效率高,就略勝Serializable了。
既然是進程間通訊,就必定會涉及到兩個或兩個進程以上,咱們知道,在Android中,啓動一個應用就表明啓動了一個進程,但其實除了啓動多個應用外,其實在一個應用中,也能夠存在多個進程,只要你爲四大組件在AndroidMenifest中指定「android:process」屬性就行,好比我但願ClientActivity啓動的時候,運行在獨立的進程,我就會在AndroidMenifest中這樣寫,以下:
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".ClientActivity" android:process="com.example.aidltest.client"/>
複製代碼
能夠看到我只是簡單的ClientActivity指定了「android:process」屬性,這樣ClientActivity在啓動的時候就會運行在進程名爲com.example.aidltest.client的獨立進程上,而MainActivity啓動時,就會運行在默認進程上,默認進程的進程名爲當前包名即com.example.aidltest。
須要注意的是,此時這兩個Activity雖然在同一個應用,可是卻不在同一個進程,因此它們是不共享內存空間的,Android會爲每個進程分配一個獨立的虛擬機,不一樣的虛擬機在內存分配上有不一樣的地址空間,因此MainActivity和ClientActivity的內存地址空間不同,它們要訪問對方的數據的時候,都要經過進程間通訊的方式來進行。
接下來爲了方便後面的講解,我把ClientActivity指定爲主活動,把MainActivity刪除,再建立一個Server,併爲它指定process屬性,以下:
<activity android:name=".ClientActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".RemoteService" android:enabled="true" android:exported="true" android:process="com.example.aidltest.remote"/>
複製代碼
ClientActivity啓動時會運行在默認的進程上,RemoteService啓動時會運行在名爲com.example.aidltest.remote的進程上,它們在本文分別表明爲本地客戶端和遠程服務端。
前面花了一點篇幅來說解序列化和其餘進程間通訊的方式,主要是讓你們有個心理準備,下面進入正文:
它全稱是Android Interface Definition Language,即Android接口定義語言,爲了使其餘的進程也能夠訪問本進程提供的服務,Android使用AIDL來公開服務的接口,它裏面定義了本進程能夠爲其餘進程提供什麼服務,即定義了一些方法,其餘進程就能夠經過RPC(遠程調用)來調用這些方法,從而得到服務,其中提供服務的進程稱爲服務端,獲取服務的進程稱爲客戶端。
AIDL接口用來暴露服務點提供給客戶端的方法,新建一個AIDL接口文件,只須要在你的項目中 點擊包名 -> 右鍵 -> new -> AIDL -> Aidl.file,而後輸入AIDL接口名稱,這裏我輸入了IUserManager,而後點擊Finish,就會在你的main目錄下建立了一個aidl文件夾,aidl文件夾裏的包名和java文件夾裏的包名相同,裏面用來存放AIDL接口文件,以下:
在裏面你會發現你剛剛建立的AIDL接口IUserManager,點進去,以下:
// IUserManager.aidl
package com.example.aidltest;
// Declare any non-default types here with import statements
interface IUserManager {
/** * 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支持的一些數據類型(int、long、boolean、float、double、String),除了這些,AIDL還支持其餘的基本數據類型、ArrayList(裏面的每一個元素都要被AIDL支持)、HashMap(裏面的每一個元素都要被AIDL支持)、實現了Parcelable接口的對象和AIDL接口自己,還有AIDL接口中只支持聲明方法,不支持聲明靜態常量。
其中若是要在AIDL接口文件中使用AIDL對象,必須顯式的 import 進來,即便它們在同一個包內,還有若是在AIDL接口文件用到了Parcelable對象,必須新建一個和它同名的AIDL文件,並在其中聲明它爲parcelable類型,接下來我要使用User這個Parcelable對象,因此我要在aidl文件夾下新建一個和他同名的AIDL文件,以下:
而後在User.aidl中添加以下內容:
// User.aidl
package com.example.aidltest;
parcelable User;
複製代碼
在裏面,我聲明瞭User.java這個對象爲parcelable類型,接下來把IUserManager中的basicTypes方法刪除,添加一個根據用戶姓名得到用戶信息的方法,以下:
// IUserManager.aidl
package com.example.aidltest;
import com.example.adiltest.User;
interface IUserManager {
User getUser(String name);
}
複製代碼
在裏面我顯示的 import 了User這個AIDL文件,即便它們在同一個包內,並聲明瞭一個getUser方法,這個方法將會在服務端實現,而後在客戶端調用(RPC)。
有了AIDL接口後咱們須要根據AIDL接口生成客戶端和服務端對應的Binder類,有兩種方式生成,一種是經過SDK自動生成,另一種是咱們本身手動編碼實現,其中可以進行手動編碼實現的前提是基於對SDK自動生成的各類Binder類的充分理解,下面咱們先來介紹SDK自動生成的Binder類。
咱們在AS導航欄 Build -> ReBuild Project,SDK就會替咱們在 app\build\generated\aidl_source_output_dir\debug\compileDebugAidl\out\包名 下生成一個IUserManager.java,它就是根據IUserManager.aidl文件生成的,裏面沒有縮進,因此看起來不習慣,使用快捷鍵ctrl+alt+L,格式化一下代碼,以下:
//IUserManager.java
/** * 一、IUserManager接口,getUser方法定義在其中 **/
public interface IUserManager extends android.os.IInterface {
/** * 一、抽象類Stub,須要在遠程服務端實現 */
public static abstract class Stub extends android.os.Binder implements com.example.aidltest.IUserManager {
private static final java.lang.String DESCRIPTOR = "com.example.aidltest.IUserManager";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static com.example.aidltest.IUserManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.aidltest.IUserManager))) {
return ((com.example.aidltest.IUserManager) iin);
}
return new com.example.aidltest.IUserManager.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 {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getUser: {//case TRANSACTION_getUser分支
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
//調用getUser方法的具體實現
com.example.aidltest.User _result = this.getUser(_arg0);
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
/** * 二、代理類Proxy,客戶端使用 */
private static class Proxy implements com.example.aidltest.IUserManager {
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 com.example.aidltest.User getUser(java.lang.String name) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.example.aidltest.User _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
//傳進了TRANSACTION_getUser字段
mRemote.transact(Stub.TRANSACTION_getUser, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
_result = com.example.aidltest.User.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public com.example.aidltest.User getUser(java.lang.String name) throws android.os.RemoteException;
}
複製代碼
這個文件有3個主要的類:
一、IUserManager接口
它聲明瞭IUserManager.aidl定義的getUser方法,繼承自IInterface。
二、IUserManager.Stub抽象類
IUserManager的靜態內部類,它繼承自Binder,說明它是一個 Binder 本地對象(Binder下面介紹),它雖然實現了IUserManager接口,可是它繼續聲明爲一個抽象類,並無實現IUserManager接口中的getUser方法,代表子類須要實現getUser方法,返回具體的User信息,而服務端將會實現這個Stub抽象類。
**三、IUserManager.Stub.Proxy代理類 **
IUserManager.stub的靜態內部類,它實現了IUserManager接口,而且實現了getUser方法,可是裏面只是把數據裝進data這個Parcel對象,經過mRemote的transact方法發送給服務端,接着用reply這個Parcel對象等待服務端數據的返回,這一切都是經過mRemote這個IBinder對象進行,mRemote表明着Binder對象的本地代理(IBinder下面介紹),mRemote會經過Binder驅動來完成與遠程服務端的Stub的通訊。
能夠看到Stub類和Stub.Proxy類都實現了IUserManager接口,這就是一個典型的代理模式,它們的getUser方法有着不一樣的實現,Stub類它將會在遠程的服務端完成getUser方法的具體實現,而Stub.Proxy類是本地客戶端的一個代理類,它已經替咱們默認的實現了getUser方法,該方法裏面經過mRemote這個Binder引用的transact方法把請求經過驅動發送給服務端,咱們注意到mRemote發送請求時還傳進了TRANSACTION_getUser這個表明着getUser方法的標識名,這表示客戶端告訴服務端我要調用getUser這個方法,當驅動把請求轉發給服務端後,服務端的Stub類的onTransact方法就會回調,它裏面有一個switch語句,根據code來調用不一樣的方法,這時它就會走到case TRANSACTION_getUser這個分支,而後調用getUser方法的在服務端的具體實現,若是有返回值的話,還會經過reply返回給客戶端,這樣就經過Binder驅動完成了一次遠程方法調用(RPC)。
這裏要注意的是客戶端經過mRemote的transact方法把請求發送給客戶端以後,這時會阻塞UI線程等待服務端的返回,而服務端的onTransact方法回調時,服務端的getUser方法會被回調,這時服務端的getUser方法是運行在服務端Binder線程池中,因此若是此時有UI操做須要回到UI線程再進行UI操做。
咱們還注意到IUserManager接口繼承了IInterface,IUserManager.Stub繼承自Binder,它們是幹什麼的?咱們來認識一下:
IInterface
這是一個接口,用來表示服務端提供了哪些服務,若是服務端須要暴露調用服務的方法給客戶端使用,就必定要繼承這個接口,它裏面有個asBinder方法,用於返回當前的Binder對象。
IBinder
這時一個跨進程通訊的Base接口,它聲明瞭跨進程通訊須要實現的一系列抽象方法,實現了這個接口就說明能夠進行跨進程通訊,Binder和BinderProxy都繼承了這個接口。
Binder
表明的是 Binder 本地對象(Binder實體),它繼承自IBinder,全部本地對象都要繼承Binder,Binder中有一個內部類BinderProxy,它也繼承自IBinder,它表明着Binder對象的本地代理(Binder引用),Binder實體只存在於服務端,而Binder引用則遍及於各個客戶端。
接下來咱們動手實踐一下,首先在服務端RemoteService中,咱們要這樣作:
public class RemoteService extends Service {
//一、實現Stub類中的getUser方法
private IUserManager.Stub mBinder = new IUserManager.Stub() {
@Override
public User getUser(String name) throws RemoteException {
//這裏只是簡單的返回了一個用戶名爲name,密碼爲123456的用戶實例
return new User(name, "123456");
}
};
public RemoteService() {
}
@Override
public IBinder onBind(Intent intent) {
//二、在onBinder方法中返回Stub類的實現,Stub類繼承自Binder,Binder實現了IBinder,這樣返回是沒問題的
return mBinder;
}
}
複製代碼
在服務端咱們須要實現Stub類中的getUser方法,而後在onBinder方法中返回Stub類的實現,這樣客戶端綁定服務時就會收到這個Stub類的Binder引用。
而後在客戶端ClientActivity中,咱們要這樣作:
public class ClientActivity extends AppCompatActivity {
private static final String TAG = ClientActivity.class.getSimpleName();
//一、建立ServiceConnection
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected, 與服務端鏈接成功!");
//把服務端返回的Binder引用,經過Stub.asInterface方法包裝成本地代理類IUserManager.Stub.Proxy,Proxy類實現了IUserManager,因此這樣寫是沒問題的
IUserManager userManager = IUserManager.Stub.asInterface(service);
try {
//經過本地代理對象遠程調用服務端的方法
User user = userManager.getUser("rain");
Log.d(TAG, "onServiceConnected,向服務端獲取用戶信息成功,User = ["
+ "name = " + user.getName()
+ "password = " + user.getPassword());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected, 與服務端斷開鏈接");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//二、啓動並經過ServiceConnection綁定遠程服務
bindService(
new Intent(this, RemoteService.class),
mServiceConnection,
Context.BIND_AUTO_CREATE
);
}
@Override
protected void onDestroy() {
super.onDestroy();
//三、解綁服務
unbindService(mServiceConnection);
}
}
複製代碼
在服務端咱們首先要建立ServiceConnection,而後經過ServiceConnection來綁定RemoteService,在成功綁定後,ServiceConnection的onServiceConnected方法就會回調,它的第二個輸入參數就是RemoteService在onBind方法返回的Stub類的Binder引用,咱們拿到這個引用後,就能夠經過經過Stub.asInterface方法轉換爲本地代理類Stub.Proxy,而後調用它的getUser方法,Proxy的getUser方法會遠程調用RemoteService的getUser方法,方法返回後,在log中打印出User的信息,最後,活動結束,咱們記得解綁服務,這個過程和上面介紹的一次RPC過程是同樣的。
咱們發現完成一次進程間的通訊是很是的簡單,這就好像只是簡單的調用一個對象的方法,但其實這都得益於Binder在底層爲咱們作了更多工做,咱們上層使用得有多簡單,它得底層實現就有多複雜,上面的一次進程間通訊,能夠簡單的用下圖表示:
其中ServiceManager就是根據Binder的名字查找Binder引用並返回,若是你對Binder的通訊架構有必定的瞭解,理解這個就不難,對象轉換就是完成Binder實體 -> Binder引用,Binder引用 -> Binder實體的轉換,在java層繼承自Binder的都表明Binder本地對象,即Binder實體,而Binder類的內部類BinderProxy就表明着Binder對象的本地代理,即Binder引用,這兩個類都繼承自IBinder, 於是都具備跨進程傳輸的能力,在跨越進程的時候,Binder 驅動會自動完成這兩個對象的轉換。
咱們重點講一下圖中的第3步:將Binder引用賦值給Proxy的mRemote字段,Proxy就是前面介紹的Stub.Proxy,因此接着咱們看看**IUserManager.Stub.asInterface(IBinder)**方法是如何把服務端返回的Binder引用賦值給本地的代理類Proxy的mRemote字段,asInterface方法以下:
//IUserManager.Stub.java
public static com.example.aidltest.IUserManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
//根據DESCRIPTOR調用IBinder的queryLocalInterface方法
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.aidltest.IUserManager))) {
return ((com.example.aidltest.IUserManager) iin);
}
return new com.example.aidltest.IUserManager.Stub.Proxy(obj);
}
//Binder
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
if (mDescriptor != null && mDescriptor.equals(descriptor)) {
//mOwner等於Stub類實例
return mOwner;
}
return null;
}
//Binder#BinderProxy
public IInterface queryLocalInterface(String descriptor) {
return null;
}
複製代碼
能夠發現它裏面根據DESCRIPTOR標識調用IBinder的queryLocalInterface方法在查找一些什麼,DESCRIPTOR是什麼?DESCRIPTOR是Binder實體的惟一標識,通常用當前的Binder的類名錶示,它定義在Stub類中,如本文的Stub的 DESCRIPTOR = "com.example.aidltest.IUserManager",前面已經講過Binder和BinderProxy都繼承自IBinder,因此它們的queryLocalInterface有不一樣的實現,咱們看到BinderProxy的直接返回null;而Binder的須要和本身的DESCRIPTOR比較,若是相同就返回mOwner,不然返回null,其中mOwner就等於Stub類實例,在Stub類構造的時候賦值,以下:
public static abstract class Stub extends android.os.Binder implements com.example.aidltest.IUserManager {
private static final java.lang.String DESCRIPTOR = "com.example.aidltest.IUserManager";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
//...
}
複製代碼
這說明了若是queryLocalInterface的返回不爲空,iin != null,表示obj是Binder實體(Stub的子類),客戶端和服務端在同一個進程,asInterface方法返回的就是Binder實體;若是queryLocalInterface的返回爲空,iin == nul,表示obj其實是Binder引用,客戶端和服務端在不一樣的進程,asInterface構造一個Proxy對象返回,並把Binder引用經過構造傳了進去,咱們看Proxy的構造函數,以下:
private static class Proxy implements com.example.aidltest.IUserManager {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
//...
}
複製代碼
能夠看到,傳進去的Binder引用賦值給了mRemote字段,因此Proxy中的mRemote就是Binder引用,客戶端就是經過這個mRemote來和服務端通訊。
也就是說,若是在同一個進程,asInterface返回的是Stub的實現類,由於不存在跨進程調用,直接調用該方法就行,若是在不一樣進程,asInterface返回的是Proxy對象,客戶端調用Proxy中的同名方法,經過mRemote的transact方法掛起當前線程等待服務端返回,服務端收到請求後響應返回數據。
到這裏,相信你們在SDK把幫助下已經會使用AIDL來完成簡單的進程間通訊,接下來經過手動編碼實現。
咱們發現使用AIDL系統會自動的幫咱們生成上述代碼,是爲了方便咱們的開發,系統根據AIDL文件生成的java文件格式是固定,咱們徹底能夠拋開AIDL直接手寫對應的Binder類,下面咱們本着單一原則把本來IUserManager.java的裏面的Stub類和Stub類中的Proxy類獨立出來,因此咱們總共要寫3個類,分別是:IUserManager、Stub、Proxy。
一、聲明一個繼承自IInterface的接口
聲明一個繼承自IInterface的接口,在裏面定義咱們想要讓客戶端調用的方法,以下:
public interface IUserManager extends IInterface {
User getUser(String name);
}
複製代碼
二、聲明服務端的Stub類
Stub類須要繼承Binder,代表它是一個Binder本地對象,它還須要實現IUserManager接口,可是繼續聲明爲抽象類,不須要實現IUserManager的getUser方法,接着咱們作如下幾步:
一、在裏面定義一個字符串DESCRIPTOR,表示Binder實體的惟一標識,用當前的Stub類的類名錶示,並把它的可見修飾符改成public,待會在Proxy須要用到.
二、在構造函數中把this 和 DESCRIPTOR字符串 attach 給父類Binder中的mOwner和mDescriptor字段.
三、定義一個TRANSACTION_getUser整型數值表明着getUser方法的標識名,賦值格式照抄自動生成的Stub類的TRANSACTION_getUser.
四、定義一個asInterface靜態方法,裏面的內容實現照抄自動生成的Stub類的asInterface方法,須要注意裏面的IUserManager接口須要換成咱們剛剛定義的IUserManager接口.
五、最後重寫IInterface的asBinder方法和Binder的onTransact方法,裏面的內容實現照抄自動生成的Stub類的asBinder和onTransact方法.
最終這個Stub類以下:
public abstract class Stub extends Binder implements IUserManager {
public static final String DESCRIPTOR = "com.example.aidltest.Stub";//這裏改爲com.example.aidltest.Stub
static final int TRANSACTION_getUser = (IBinder.FIRST_CALL_TRANSACTION + 0);
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static IUserManager asInterface(android.os.IBinder obj) {//導入咱們自定義的IUserManager
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IUserManager))) {
return (IUserManager) iin;
}
return new 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 {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getUser: {
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
com.example.aidltest.User _result = this.getUser(_arg0);
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
}
複製代碼
三、聲明客戶端的Proxy類
Proxy類只須要實現IUserManager接口,而且實現IUserManager中的getUser方法,接着咱們作如下幾步:
一、定義一個IBinder類型的mRemote字段,並在構造函數中賦值.
二、實現IUserManager中的getUser方法和IInterface的asBinder方法,裏面的內容實現照抄自動生成的Stub.Proxy的getUser方法和asBinder方法,須要注意getUser中的一些字段須要導入咱們剛剛在Stub類中定義的字段.
最終這個Proxy類以下:
public class Proxy implements IUserManager {
private IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
@Override
public com.example.aidltest.User getUser(java.lang.String name) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.example.aidltest.User _result;
try {
_data.writeInterfaceToken(Stub.DESCRIPTOR);//這裏引用咱們本身寫的Stub的DESCRIPTOR
_data.writeString(name);
mRemote.transact(Stub.TRANSACTION_getUser, _data, _reply, 0);//這裏引用咱們本身寫的Stub的TRANSACTION_getUser
_reply.readException();
if ((0 != _reply.readInt())) {
_result = com.example.aidltest.User.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
複製代碼
最終整個工程目錄以下:
四、使用
使用就很簡單了,只須要把上面自動生成的IUserManager、IUserManager.Stub、IUserManager.Stub.Proxy替換成咱們本身寫的IUserManager、Stub、Proxy就行,就再也不貼代碼了,輸出以下:
日常在Android中應用到進程間通訊的場景很是的少,但這並非說AIDL沒有用,一個最直觀的應用就是在閱讀Android系統源碼的時候,例如Activity的啓動流程:
在android8.0以後Activity的有關進程間的通訊都是經過AIDL來實現,Android根據IActivityManager.aidl文件來生成進程間通訊所需的Binder類,如ApplicationThread在AMS的本地代理,AMS在ActivityThread的本地代理,當咱們經過startActiivty發起Activity的啓動請求時,ActivityThread就會經過AMS本地代理調用AMS的相應方法,當Actiivty在AMS準備好後,AMS就會經過ActivityThread本地代理回調應用進程的Activity的生命週期方法,這裏就不在多述了,這個過程以下:
主要是經過這個例子說明,學習完AIDL後,可以幫助咱們更好的理解系統源碼有關跨進程的一些術語,類等,經過AIDL也能更好的加深咱們對Android進程間通訊的原理的理解,也掌握了一種進程間通訊的方式。
本文簡單的介紹了一下Android幾種進程間通訊的方式,而後經過SDK自動生成AIDL代碼來理解了一下生成的代碼中各個類的做用和關係,還根據自動生成AIDL代碼來手動實現了一遍簡單的跨進程通訊,加深理解,掌握了一些基礎AIDL知識,可能會有些不全面,可是足夠基本使用,想要了解更全面的AIDL知識,最好的途徑仍是參閱官方文檔:Android 接口定義語言 (AIDL)
參考資料: