Java層Binder機制詳解

本文是我閱讀《Android內核剖析》一書的筆記,在此寫下來是希望能夠加深理解,也希望朋友們能夠指出其中的不足。http://blog.csdn.net/yuanzeyao/article/details/12954641

Binder是一種基於C/S的架構,主要包括四個部分:服務端(Server),客戶端(Client),Binder驅動,ServiceManager。Binder是Android系統中非常重要的一種IPC機制,如果你想研究Frameworks,必須先對Binder機制誘有一定的認識,否則是無法看懂Frameworks源碼的。上述四個部分分別在不同的進程中,他們之間的交互都是通過Binder實現,現在站在宏觀的角度來分析Server,Client,ServiceManager之間的關係,如下圖:

                   

分析上圖:

(1)    當一個服務進程啓動後,需要將其中的Service註冊到ServiceManager中,此時底層會有兩個變化:①後臺啓動一個監聽線程,監聽Binder驅動發過來的消息 ② 在Binder驅動中生存一個Service的Binder引用

(2)    Client想和某一個Service交互,需要向ServiceManager申請獲得該Service在Binder中的引用,並存放在自身的mRemote變量中,之後通過mRemote調用。

上圖展示了客戶端如何和服務端交互:

 客戶端獲取服務端在Binder驅動中的應用,這個引用改寫了transact方法(這個方法來自於IBinder),這個方法通過調用onTransact方法實現如下功能:首先向服務端發送一個消息調用,並掛起當前線程,然後等待服務端執行完畢返回,最後繼續執行客戶端線程。

下面使用AIDL工具生成一個簡單的服務端和客戶端吧。在一個包中新建一個ImusicPlayerService.aidl文件,這時在R.java目錄下回自動生成一個ImusicPlayerService.java文件。

ImuiscPlayerService.aidl

[java] view plain copy
 print?
  1. interface IMusicPlayerService  
  2. {  
  3.     void start(String path);  
  4.     void stop();  
  5. }  

ImusicPlayerService.java文件

[java] view plain copy
 print?
  1. public interface IMusicPlayerService extends android.os.IInterface  
  2. {  
  3. /** Local-side IPC implementation stub class. */  
  4. public static abstract class Stub extends android.os.Binder implements com.binder.demo.IMusicPlayerService  
  5. {  
  6.     private static final java.lang.String DESCRIPTOR = "com.binder.demo.IMusicPlayerService";  
  7. /** Construct the stub at attach it to the interface. */  
  8.     public Stub()  
  9.     {  
  10.         this.attachInterface(this, DESCRIPTOR);  
  11.     }  
  12. /** 
  13.  * Cast an IBinder object into an com.binder.demo.IMusicPlayerService interface, 
  14.  * generating a proxy if needed. 
  15.  */  
  16.     public static com.binder.demo.IMusicPlayerService asInterface(android.os.IBinder obj)  
  17.     {  
  18.         if ((obj==null)) {  
  19.             return null;  
  20.         }  
  21.         android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);  
  22.         if (((iin!=null)&&(iin instanceof com.binder.demo.IMusicPlayerService))) {  
  23.             return ((com.binder.demo.IMusicPlayerService)iin);  
  24.         }  
  25.         return new com.binder.demo.IMusicPlayerService.Stub.Proxy(obj);  
  26.     }  
  27.     @Override   
  28.     public android.os.IBinder asBinder()  
  29.     {  
  30.     return this;  
  31.     }  
  32.     @Override   
  33.     public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException  
  34.     {  
  35.         switch (code)  
  36.         {  
  37.             case INTERFACE_TRANSACTION:  
  38.             {  
  39.                 reply.writeString(DESCRIPTOR);  
  40.                 return true;  
  41.             }  
  42.             case TRANSACTION_start:  
  43.             {  
  44.                 data.enforceInterface(DESCRIPTOR);  
  45.                 java.lang.String _arg0;  
  46.                 _arg0 = data.readString();  
  47.                 boolean _result = this.start(_arg0);  
  48.                 reply.writeNoException();  
  49.                 reply.writeInt(((_result)?(1):(0)));  
  50.                 return true;  
  51.             }  
  52.             case TRANSACTION_stop:  
  53.             {  
  54.                 data.enforceInterface(DESCRIPTOR);  
  55.                 this.stop();  
  56.                 reply.writeNoException();  
  57.                 return true;  
  58.             }  
  59.         }  
  60.         return super.onTransact(code, data, reply, flags);  
  61.     }  
  62.     private static class Proxy implements com.binder.demo.IMusicPlayerService  
  63.     {  
  64.         private android.os.IBinder mRemote;  
  65.         Proxy(android.os.IBinder remote)  
  66.         {  
  67.         mRemote = remote;  
  68.         }  
  69.         @Override   
  70.         public android.os.IBinder asBinder()  
  71.         {  
  72.             return mRemote;  
  73.         }  
  74.         public java.lang.String getInterfaceDescriptor()  
  75.         {  
  76.             return DESCRIPTOR;  
  77.         }  
  78.         @Override   
  79.         public boolean start(java.lang.String filePath) throws android.os.RemoteException  
  80.         {  
  81.             android.os.Parcel _data = android.os.Parcel.obtain();  
  82.             android.os.Parcel _reply = android.os.Parcel.obtain();  
  83.             boolean _result;  
  84.             try {  
  85.                 _data.writeInterfaceToken(DESCRIPTOR);  
  86.                 _data.writeString(filePath);  
  87.                 mRemote.transact(Stub.TRANSACTION_start, _data, _reply, 0);  
  88.                 _reply.readException();  
  89.                 _result = (0!=_reply.readInt());  
  90.             }  
  91.             finally {  
  92.                 _reply.recycle();  
  93.                 _data.recycle();  
  94.             }  
  95.             return _result;  
  96.         }  
  97.         @Override   
  98.         public void stop() throws android.os.RemoteException  
  99.         {  
  100.             android.os.Parcel _data = android.os.Parcel.obtain();  
  101.             android.os.Parcel _reply = android.os.Parcel.obtain();  
  102.             try {  
  103.                 _data.writeInterfaceToken(DESCRIPTOR);  
  104.                 mRemote.transact(Stub.TRANSACTION_stop, _data, _reply, 0);  
  105.                 _reply.readException();  
  106.             }  
  107.             finally {  
  108.                 _reply.recycle();  
  109.                 _data.recycle();  
  110.             }  
  111.         }  
  112.     }  
  113.     static final int TRANSACTION_start = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);  
  114.     static final int TRANSACTION_stop = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);  
  115. }  
  116. public boolean start(java.lang.String filePath) throws android.os.RemoteException;  
  117. public void stop() throws android.os.RemoteException;  
  118. }  

我們來重點分析一下兩個重要的類:

class Stub extends android.os.Binderimplements com.binder.demo.ImusicPlayerService

private static class Proxy implementscom.binder.demo.ImusicPlayerService


對照上面一個圖很容易知道Stub這個類對應的是Server端,而Proxy對應的是客戶端,那麼首先從客戶端分析:


在客戶端中,我們看到一個私有變量mRemote,它存放的就是服務端在Binder驅動中的一個引用。接下來分析start方法:爲了查看方便,我再次將start方法代碼貼出來:

[java] view plain copy
 print?
  1. public boolean start(java.lang.String filePath) throws android.os.RemoteException  
  2.     {  
  3.             android.os.Parcel _data = android.os.Parcel.obtain();  
  4.             android.os.Parcel _reply = android.os.Parcel.obtain();  
  5.             boolean _result;  
  6.             try {  
  7.                 _data.writeInterfaceToken(DESCRIPTOR);  
  8.                 _data.writeString(filePath);  
  9.                 mRemote.transact(Stub.TRANSACTION_start, _data, _reply, 0);  
  10.                 _reply.readException();  
  11.                 _result = (0!=_reply.readInt());  
  12.             }  
  13.             finally {  
  14.                 _reply.recycle();  
  15.                 _data.recycle();  
  16.             }  
  17.             return _result;  
  18.         }  

發現start裏面其實就是調用的mRemote中的transact方法,這不就是一種典型的代理模式嗎?我想這裏之所以使用代理模式,是爲了在將參數打包發送到服務端去之前,固定各個參數在包中的順序,方便服務端去除數據。至於其中的Stub.TRANSACTION_start是在Stub類中定義的一個變量,用來標示函數的。

static final int TRANSACTION_start =(android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

 

transact函數經改寫後,本質就是調用了Stub中的onTransact函數,那麼我們來分析一下服務端的onTransact函數吧。同樣爲了方便,再次貼出:

[java] view plain copy
 print?
  1. public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException  
  2.     {  
  3.         switch (code)  
  4.         {  
  5.             case INTERFACE_TRANSACTION:  
  6.             {  
  7.                 reply.writeString(DESCRIPTOR);  
  8.                 return true;  
  9.             }  
  10.             case TRANSACTION_start:  
  11.             {  
  12.                 data.enforceInterface(DESCRIPTOR);  
  13.                 java.lang.String _arg0;  
  14.                 _arg0 = data.readString();  
  15.                 boolean _result = this.start(_arg0);  
  16.                 reply.writeNoException();  
  17.                 reply.writeInt(((_result)?(1):(0)));  
  18.                 return true;  
  19.             }  

由於傳入的code是TRANSACTION_start,所以服務端首先從包中按照客戶端的寫入順序讀出數據,然後調用自己的start方法,也就是說我們已經完成在客戶端調用服務端的方法了,然後將返回數據寫入到reply包中,Binder驅動接到這個通知後,就會繼續執行客戶端線程從這個包中去除數據。

 

細心的朋友可能會發現一個問題,還有一個非常關鍵的問題沒有解決,客戶端是如何拿到服務端的Binder應用的,下面就來分析一下這個問題

 

在寫程序的時候,經常會使用getSystemService這個函數來獲取一個XXXManager,例如我需要獲得一個InputMethodManager,那麼我會這麼做:

[java] view plain copy
 print?
  1. <pre name="code" class="java">InputMethodManagerwm=(InputMethodManager)context.getSystemService(“input_method”);  
  2. getSystemService這個函數是在ContextImpl中實現的,部分源碼如下:  
  3. else if (INPUT_METHOD_SERVICE.equals(name)) {  
  4.             return InputMethodManager.getInstance(this);  
  5. 進入到getInstance(this)看看源碼:  
  6. static public InputMethodManager getInstance(Looper mainLooper) {  
  7.         synchronized (mInstanceSync) {  
  8.             if (mInstance != null) {  
  9.                 return mInstance;  
  10.             }  
  11.             IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);  
  12.             IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);  
  13.             mInstance = new InputMethodManager(service, mainLooper);  
  14.         }  
  15.         return mInstance;  
  16. }  
  17. </pre><br><br>  

代碼中通過使用ServiceManager中的getService方法獲取到一個Binder對象,然後通過這個Binder對象new 一個InputMethodManager出來並返回,這裏我們不得不猜想這個Binder對象就是一個服務端的引用,爲了驗證猜想,繼續進入到getService看看

[java] view plain copy
 print?
  1. public static IBinder getService(String name) {  
  2.         try {  
  3.             IBinder service = sCache.get(name);  
  4.             if (service != null) {  
  5.                 return service;  
  6.             } else {  
  7.                 return getIServiceManager().getService(name);  
  8.             }  
  9.         } catch (RemoteException e) {  
  10.             Log.e(TAG, "error in getService", e);  
  11.         }  
  12.         return null;  
  13.     }  

還挺複雜的,我們進入getIServiceManager中看看吧,估計第一次學習Binder的同學已經暈了,沒事多看幾遍就很清晰的。

[java] view plain copy
 print?
  1. private static IServiceManager getIServiceManager() {  
  2.         if (sServiceManager != null) {  
  3.             return sServiceManager;  
  4.         }  
  5.   
  6.         // Find the service manager  
  7.         sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());  
  8.         return sServiceManager;  
  9. }  

到了這裏就涉及到幾點知識點了,容我慢慢道來:

(1)    ServiceManager本身就是一個Service,那麼我們的客戶端再和它交互之前也必須獲取到它在Binder中的引用,說道這裏可能有些朋友就會想,我們不是要通過ServiceManager獲取Binder應用嗎,那麼他自己的Binder應用怎麼獲取呢,呵呵,這個問題解決起來很簡單,Android中提供了BinderInternal.getContextObject()函數返回ServiceManger的引用,說白了就是這個意思:ServiceManager的Binder引用通過上面這個函數獲得,而其他Service的Binder引用需要通過ServiceManager獲得。

(2)    這裏有一個asInterfaec函數,它的代碼大家可以查看Stub類中的asInterface,在這裏我只說明一下它的作用。前面已經說過,一個Service說到底就是一個Binder對象,一個Binder對象創建後,就會在Binder驅動中形成一個引用,客戶端就是通過這個引用和服務端交互,但是如果在這個Service進程內部調用這個Service,那麼就沒有必要使用IPC,直接可以使用這個Service對象(Binder對象),也就是說這個函數就是對外界提供了統一的接口,不管是遠程調用還是本地調用,使用的都是同樣的代碼,

 

到這裏我們會發現,在XXXManager裏面有一個IXXX類型的變量,如果是遠程調用,那麼就是Proxy累,如果是本地調用,存放的就是Stub類


總結:

(1)      Server所在進程啓動後,需要將其中的Servic註冊到ServiceManager,這樣Client就可以通過ServiceManager找到對應的Binder引用,而ServiceManager自身的Binder引用時通過BinterInternal.getContextObject()函數獲得

(2)      我們在寫程序時,通過getSystemService獲得XXXManager,在XXXManager中保存了一個IXXX變量,這個變量可能是Proxy類型,也可能是Stub類型,這個過程被asInterface函數屏蔽了。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章