標籤:
Binder:別針,回形針。生活中,我們用回形針將兩張紙“別”在一起。android中,Binder用於處理序間通訊,即將兩個進程“別”在一起。
Binder是一種架構,有3個模組(服務端介面,Binder驅動,客服端介面),如:
Binder服務端,實際上就是一個Binder類的對象,該對象一旦建立內部就會啟動一個隱藏線程。
根據上面的架構,說說我所理解的。
客服端程式要想訪問遠程服務,那麼它就需要擷取到服務端的Binder引用---mRemote。Android工程師提供了一個解決方案,那就是Service。對於客服端來講,可以使用兩種方式來和服務端建立串連。
public ComponentaName startService(Intent intent)public boolean bindService(Intent service, ServiceConnection conn, int flags)
第一種方式啟動後,客服端暫時沒有服務端Binder的引用。
而第二種方式啟動後,ServiceConnection介面的onServiceConnected方法會被回調。
ServiceConnection代碼如下:
public interface ServiceConnection { public void onServiceConnected(ComponentName name, IBinder service); public void onServiceDisconnected(ComponentName name);}
onServiceConnected方法中的IBinder對象service就是遠程服務的引用。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
我所理解的Binder機制到這裡就結束了,那麼AIDL的出現是為瞭解決什嗎?
編寫一個aidl檔案,看看它到底做了什麼。如下所示:
package com.aprz.aidl;interface IAidlTest{ void test();}
interface是關鍵字,有時在interface之前加上oneway表示service提供的方法都沒有傳回值,檔案名稱以“I”開頭是為了統一規範。檔案編寫完之後,android的開發環境會自動產生以下代碼:
/*___Generated_by_IDEA___*/ /* * This file is auto-generated. DO NOT MODIFY. * Original file: F:\\workspace\\AIDLService\\src\\com\\aprz\\aidl\\AidlTest.aidl */package com.aprz.aidl; public interface AidlTest extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.aprz.aidl.AidlTest { private static final java.lang.String DESCRIPTOR = "com.aprz.aidl.AidlTest"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.aprz.aidl.AidlTest interface, * generating a proxy if needed. */ public static com.aprz.aidl.AidlTest asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.aprz.aidl.AidlTest))) { return ((com.aprz.aidl.AidlTest) iin); } return new com.aprz.aidl.AidlTest.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_test: { data.enforceInterface(DESCRIPTOR); this.test(); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.aprz.aidl.AidlTest { 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 void test() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_test, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_test = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } public void test() throws android.os.RemoteException;}
這些程式碼完成了三個任務:
1、這個檔案本身是一個java interface,包含了aidl檔案所聲明的服務函數(檔案最下面)。
2、定義了一個Proxy類,該類作為客服端訪問服務端的代理,主要是為了統一包裹內寫入參數的順序(當我們在客服端使用Stub的asInterface將IBinder轉換為具體的介面以便調用服務端的方法時,其實返回的Proxy對象,代理做的主要工作就是統一包裹內寫入參數的順序)。
這裡需要詳情解釋一下這句話的意思。我們知道服務端是一個Binder對象,假設讓我們來設計一個Service端,這個Service只提供兩個方法:start(String filePath)和stop()。
那麼其代碼大致如下:
public class MusicPlayService extends Binder{ public void start(String filePath) { } public void stop(){ } //服務端必須要實現onTransact()方法 @override protected boolean onTransact(int code, Parcel data, Pacel reply, int flags) throws RemoteException { return super.onTransact(code, data, reply, flags); }}
onTransact的第二個參數data,就是客服端傳遞過來的參數,那麼我如何正確讀取這個參數呢(假設這裡有多個參數)?
正確的讀取方式應該是這樣的:
data.enforceInterface(“MusicPlayService”);String filePath = data.readString();start(filePath);
enforceInterface()是為了某種校正,與客服端的writeInterfaceToken()方法對應。由於我寫的aidl檔案並沒有帶參數,從它產生的程式碼中也無法看出什麼,你可以自己做測試,給aidl的檔案的方法加上多個參數,看看產生的程式碼中Stub和Proxy類的方法,對比一下。
3、定義一個Stub類,主要由服務端來使用。這個類之所以是抽象類別,是因為具體的函數服務必須由程式員來實現,如下:
public class AIDLService extends Service { @Override public IBinder onBind(Intent intent) { return mBinder; } private final AidlTest.Stub mBinder = new AidlTest.Stub() { public void test() throws RemoteException { Log.e("aidl", "輸出從服務端"); } };}
我們需要返回一個IBinder對象,這個mBinder對象是Stub的一個執行個體對象,需要實現其抽象方法,Binder與IBinder的關係:public class Binder implements IBinder 。
另外,Stub中的一些常量,如TRANSACTION_test,這些常量與服務函數相對應,因為服務端收到調用訊息之後,就會執行onTransact()方法,這個時候,就根據這個常量來判斷,該執行哪個方法(TRANSACTION_test表示需要執行服務端的test方法)。Stub中還有一個asInterface函數,該方法返回一個Binder對象,需要注意的是,如果是從遠程來擷取服務端的Binder引用,會返回Binder驅動中的對象,如果是從服務端進程內部擷取,則會返回服務端本身的Binder對象(當建立一個Binder對象的時候,服務端會建立一個,Binder驅動也會建立一個)。
android的Binder