TShopping

 找回密碼
 註冊
搜索
查看: 1297|回復: 1

[教學] 兩種AIDL用法分析 IInterface

[複製鏈接]
發表於 2014-12-9 23:24:00 | 顯示全部樓層 |閱讀模式
 
Push to Facebook Push to Plurk Push to Twitter 
我們在前面介紹了關於AIDL的兩種用法,第一種用法主要用在應用層中(應用層的AIDL調用),第二種用法主要用在framework中(Framework中的AIDL調用)。但是這兩種用法中都提到了Stub、asInterface等關鍵字,在這一節中我們主要針對AIDL內部機制的分析來深入理解AIDL調用的過程。
        為了便於分析,我們還拿第一節中我們自己搭建的例子去分析。
        當時我們在Eclipse Android工程的Java包目錄中建立了一個擴展名為aidl的文件(IMyService.aidl),並寫下需要的接口。Eclipse會在gen目錄下生成一個IMyService.Java的文件。
一、整體結構

        我們打開IMyService.Java文件查看,發現他的結構是這樣的:

  1. public interface IMyService extends android.os.IInterface {
  2.       //可以看出,Stub其实是一个实现了IMyService的Binder抽象类。
  3.       public static abstract class Stub extends android.os.Binder implements aidl.pac.IMyService {
  4.           public Stub() {
  5.           }
  6.           //把远程Service的Binder对象传递进去,得到的是远程服务的本地代理
  7.           public static aidl.pac.IMyService asInterface(android.os.IBinder obj) {
  8.           }
  9.           public android.os.IBinder asBinder() {
  10.           }
  11.           //两个不同进程之间传递是通过onTransact接口完成的
  12.           public boolean onTransact(int code, android.os.Parcel data,    android.os.Parcel reply, int flags)
  13.                   throws android.os.RemoteException {
  14.           }
  15.           //远程服务的本地代理,当然也会继承自IMyService
  16.           private static class Proxy implements aidl.pac.IMyService {
  17.               private android.os.IBinder mRemote;
  18.               //构造函数
  19.               Proxy(android.os.IBinder remote) {
  20.               }
  21.               public android.os.IBinder asBinder() {
  22.               }
  23.               public java.lang.String getInterfaceDescriptor() {
  24.               }
  25.               //调用的getValue其实就是这里。
  26.               public java.lang.String getValue()
  27.                       throws android.os.RemoteException {
  28.           }
  29.           //可以看到,系统将我们的方法转换成以FIRST_CALL_TRANSACTION为基准的数字。
  30.           static final int TRANSACTION_getValue = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
  31.       }
  32.       public java.lang.String getValue() throws android.os.RemoteException;
  33.   }
複製代碼

     下面是一個結構圖:

1.gif

             整體上來看,IMyService.java文件中的IMyService類有一個Stub的內部類,還有一個getValue方法。這個方法就是我們在aidl中定義的方法。同時我們看到,生成的IMyService繼承自IInterface,說明這也是一個接口,並沒有對getValue進行實現。

        對於getValue這個方法,當我們在服務端的內部類中繼承了IMyService.Stub抽像類以後,就需要對未曾實現的getValue方法進行定義。
        再來看IMyService的內部類Stub。這個內部類是一個實現了IMyService接口的Binder抽像類。內部有3個方法asInterface、asBinder、onTransact,還有一個內部類Proxy。

二、得到的遠端Service對象

        現在我們來看一下客戶端當初得​​到的服務端對象的情況,當我們連接上服務端后,會被動調用onServiceConnected方法:

  1. public void onServiceConnected(ComponentName name, IBinder service) {
  2.             mIMyService = IMyService.Stub.asInterface(service);
  3.         }
複製代碼

在這裡得到的service對像其實就是MyServiceImpl對象,他是實現了IMyService.Stub具體接口的IBinder

再來看一下asInterface:

  1. public static aidl.pac.IMyService asInterface(android.os.IBinder obj) {
  2.             //通过asInterface得到的其实是远程Service的本地“代理”,而代理对象的参数是远程的服务端“obj”
  3.             return new aidl.pac.IMyService.Stub.Proxy(obj);
  4.         }
複製代碼

可以看出,onServiceConnected參數是遠程服務端的IBinder對象,返回值是IMyService.Stub.Proxy(obj)

這裡的Proxy是Stub的內部類:

  1. private static class Proxy implements aidl.pac.IMyService {}
複製代碼

內部除了構造方法以外,只有getValue方法:

  1. //调用的getValue其实就是这里。
  2.         public java.lang.String getValue()    throws android.os.RemoteException {
  3.             _data.writeInterfaceToken(DESCRIPTOR);
  4.             //mRemote是远程的服务对象的binder,通过transact与服务端交换数据
  5.             mRemote.transact(Stub.TRANSACTION_getValue, _data, _reply,0);
  6.             _reply.readException();
  7.             _result = _reply.readString();
  8.             return _result;
  9.         }
複製代碼

到這裡我們看到,Proxy內部確實擁有服務端的各個方法,但這些方法並不是真實的實現,而只是通過mRemote.transact傳輸出去。


        也就是說,在客戶端通過mIMyService = IMyService.Stub.asInterface(service)得到的就是Proxy對象,可以通過這個對象間接的調用服務端的各個方法,而具體調用過程就是經過mRemote.transact傳輸給真正的Service (也就是MyService.MyServiceImpl類)
        那麼,具體來說,我們是如何通過這個代理對象調用到真實的getValue呢?

三、如何通過代理對象調用遠端Service方法

        在代理類的getValue方法中看到,他是調用了mRemote.transact的方法。

        而mRemote.transact的方法通過底層的Binder通訊,將數據傳輸給服務端進程,並調用服務端的onTransact方法:

  1. public boolean onTransact(int code, android.os.Parcel data,    android.os.Parcel reply, int flags){
  2.             switch (code) {
  3.                 //客户端调用的getValue方法,最终由这里传输给服务端
  4.                 case TRANSACTION_getValue: {
  5.                     data.enforceInterface(DESCRIPTOR);
  6.                     java.lang.String _result = this.getValue();
  7.                     reply.writeNoException();
  8.                     reply.writeString(_result);
  9.                     return true;
  10.                 }
  11.             }
  12.             return super.onTransact(code, data, reply, flags);
  13.         }
複製代碼

因為當初得到的遠程服務對像是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


 

臉書網友討論
發表於 2015-6-17 10:35:11 | 顯示全部樓層



  知道了 ~~~

版主招募中

您需要登錄後才可以回帖 登錄 | 註冊 |

本版積分規則



Archiver|手機版|小黑屋|免責聲明|TShopping

GMT+8, 2016-12-6 02:40 , Processed in 0.091555 second(s), 24 queries .

本論壇言論純屬發表者個人意見,與 TShopping綜合論壇 立場無關 如有意見侵犯了您的權益 請寫信聯絡我們。

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回復 返回頂部 返回列表