我們在前面介紹了關於AIDL的兩種用法,第一種用法主要用在應用層中(應用層的AIDL調用),第二種用法主要用在framework中(Framework中的AIDL調用)。但是這兩種用法中都提到了Stub、asInterface等關鍵字,在這一節中我們主要針對AIDL內部機制的分析來深入理解AIDL調用的過程。
為了便於分析,我們還拿第一節中我們自己搭建的例子去分析。
當時我們在Eclipse Android工程的Java包目錄中建立了一個擴展名為aidl的文件(IMyService.aidl),並寫下需要的接口。Eclipse會在gen目錄下生成一個IMyService.Java的文件。
一、整體結構 我們打開IMyService.Java文件查看,發現他的結構是這樣的: - public interface IMyService extends android.os.IInterface {
- //可以看出,Stub其实是一个实现了IMyService的Binder抽象类。
- public static abstract class Stub extends android.os.Binder implements aidl.pac.IMyService {
- public Stub() {
- }
- //把远程Service的Binder对象传递进去,得到的是远程服务的本地代理
- public static aidl.pac.IMyService asInterface(android.os.IBinder obj) {
- }
- public android.os.IBinder asBinder() {
- }
- //两个不同进程之间传递是通过onTransact接口完成的
- public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
- throws android.os.RemoteException {
- }
- //远程服务的本地代理,当然也会继承自IMyService
- private static class Proxy implements aidl.pac.IMyService {
- private android.os.IBinder mRemote;
- //构造函数
- Proxy(android.os.IBinder remote) {
- }
- public android.os.IBinder asBinder() {
- }
- public java.lang.String getInterfaceDescriptor() {
- }
- //调用的getValue其实就是这里。
- public java.lang.String getValue()
- throws android.os.RemoteException {
- }
- //可以看到,系统将我们的方法转换成以FIRST_CALL_TRANSACTION为基准的数字。
- static final int TRANSACTION_getValue = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
- }
- public java.lang.String getValue() throws android.os.RemoteException;
- }
複製代碼
下面是一個結構圖: 整體上來看,IMyService.java文件中的IMyService類有一個Stub的內部類,還有一個getValue方法。這個方法就是我們在aidl中定義的方法。同時我們看到,生成的IMyService繼承自IInterface,說明這也是一個接口,並沒有對getValue進行實現。 對於getValue這個方法,當我們在服務端的內部類中繼承了IMyService.Stub抽像類以後,就需要對未曾實現的getValue方法進行定義。
再來看IMyService的內部類Stub。這個內部類是一個實現了IMyService接口的Binder抽像類。內部有3個方法asInterface、asBinder、onTransact,還有一個內部類Proxy。
二、得到的遠端Service對象 現在我們來看一下客戶端當初得到的服務端對象的情況,當我們連接上服務端后,會被動調用onServiceConnected方法: - public void onServiceConnected(ComponentName name, IBinder service) {
- mIMyService = IMyService.Stub.asInterface(service);
- }
複製代碼在這裡得到的service對像其實就是MyServiceImpl對象,他是實現了IMyService.Stub具體接口的IBinder。 再來看一下asInterface: - public static aidl.pac.IMyService asInterface(android.os.IBinder obj) {
- //通过asInterface得到的其实是远程Service的本地“代理”,而代理对象的参数是远程的服务端“obj”
- return new aidl.pac.IMyService.Stub.Proxy(obj);
- }
複製代碼
可以看出,onServiceConnected參數是遠程服務端的IBinder對象,返回值是IMyService.Stub.Proxy(obj), 這裡的Proxy是Stub的內部類: - private static class Proxy implements aidl.pac.IMyService {}
複製代碼
內部除了構造方法以外,只有getValue方法: - //调用的getValue其实就是这里。
- public java.lang.String getValue() throws android.os.RemoteException {
- _data.writeInterfaceToken(DESCRIPTOR);
- //mRemote是远程的服务对象的binder,通过transact与服务端交换数据
- mRemote.transact(Stub.TRANSACTION_getValue, _data, _reply,0);
- _reply.readException();
- _result = _reply.readString();
- return _result;
- }
複製代碼到這裡我們看到,Proxy內部確實擁有服務端的各個方法,但這些方法並不是真實的實現,而只是通過mRemote.transact傳輸出去。
也就是說,在客戶端通過mIMyService = IMyService.Stub.asInterface(service)得到的就是Proxy對象,可以通過這個對象間接的調用服務端的各個方法,而具體調用過程就是經過mRemote.transact傳輸給真正的Service (也就是MyService.MyServiceImpl類)。
那麼,具體來說,我們是如何通過這個代理對象調用到真實的getValue呢?
三、如何通過代理對象調用遠端Service方法 在代理類的getValue方法中看到,他是調用了mRemote.transact的方法。 而mRemote.transact的方法通過底層的Binder通訊,將數據傳輸給服務端進程,並調用服務端的onTransact方法: - public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags){
- switch (code) {
- //客户端调用的getValue方法,最终由这里传输给服务端
- case TRANSACTION_getValue: {
- data.enforceInterface(DESCRIPTOR);
- java.lang.String _result = this.getValue();
- reply.writeNoException();
- reply.writeString(_result);
- return true;
- }
- }
- return super.onTransact(code, data, reply, flags);
- }
複製代碼
因為當初得到的遠程服務對像是MyServiceImpl的對象,因此這裡的this就指向了MyServiceImpl類。因此getValue方法就進入到了MyServiceImpl的內部,也就是遠程服務端的內部。由此完成了一次完整的調用過程。
四、AIDL總結 1、AIDL要是實現的最終目標是跨進程訪問,簡單的說就是得到另一個進程的對象,並調用其方法。
2、AIDL與接口類似,本質屬性都是一個Interface(AIDL文件是IInterface,而Interface是繼承自Interface的),而且都只定義了抽象方法,沒有具體的實現,需要子類去實現。
3、與接口不同的是:由AIDL生成的stub類本質上是一個Binder!這個類所生成的對像有兩種方式可以傳遞給另外一個進程:
3.a、一種是通過bindService的方式,綁定一個服務,而在綁定後,服務將會返回給客戶端一個Binder的對象,此時可以把繼承自stub的Binder傳遞給客戶端。
3.b、另外一種就是把繼承自stub的類提升為系統服務,此時,我們通過ServiceManager去得到當前的系統服務,ServiceManager就會把目標Service的Binder對像傳遞給客戶端。
4、經過上面兩種方法得到的Binder對象,就像得到了本地的某個對像一樣,可以調用其遠程的方法。
http://blog.csdn.net/u010961631/article/details/12082161
|