想要掌握一樣東西, 最好的方式就是閱讀理解它的源碼。 想要掌握Android Binder, 最好的方式就是寫一個AIDL檔, 然後查看其生成的代碼。 本文的思路也是來自於此。
簡介Binder是Android常用的一種進程間通信方式。 當然, 不使用Binder, 你還可以使用Socket甚至檔來進行通信。
通常Android上的進程間通信, 指的就是遠端Service的調用。
開始新建測試工程打開Android Studio新建IPCClient和IPCServer兩個app工程。
假設我們要做這樣一件事情:
Client向Server發起一個請求:請告訴我1+2等於多少
Server將答案返回給Client
創建遠端ServiceIPCServer新建ManualCalculatorService作為遠端Service。
遠端Server需要重寫onBind。
public class ManualCalculatorService extends Service{ @Nullable @Override public IBinder onBind(Intent intent) { return new Binder { @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { return super.onTransact(code, data, reply, flags); } }; }}然後在AndroidManifest中註冊這個Service。
android:exported="true"表示這個Service對外是暴露的。
android:process=":manualremote"表示這個Service的運行進程的名稱
一個Service要作為遠端Service被其他Client調用, 上面兩個缺一不可。
創建ClientClient調用bindService即可和遠端Service建立聯繫。
Intent intent = new Intent;intent.setComponent(new ComponentName("cn.zmy.ipcserver", "cn.zmy.ipcserver.ManualCalculatorService"));bindService(intent, new ServiceConnection{ @Override public void onServiceConnected(ComponentName name, IBinder service) { } @Override public void onServiceDisconnected(ComponentName name) { }}, Context.BIND_AUTO_CREATE);至此, 兩個專案大體代碼結構已經完成。
Client調用ServerClient可以通過onServiceConnected中的IBinder類型的service參數來調用遠端Service。
Parcel data = Parcel.obtain;Parcel reply = Parcel.obtain;data.writeInt(1);data.writeInt(2);try{ service.transact(1000, data, reply, 0);}catch (RemoteException e){ e.printStackTrace;}int result = reply.readInt;data.recycle;reply.recycle;Toast.makeText(MainActivity.this, "" + result, Toast.LENGTH_SHORT).show;代碼很簡單, 最關鍵的是這一句:
service.transact(1000, data, reply, 0);
第一個參數, 1000。 這是我隨便寫的個數字, 你可以寫2000,3000都沒得問題。 (實際項目中通常使用常量定義, 這裡主要為了方便演示)
第二個參數, data。 表示我想要傳遞給Server的資料。
第三個參數, reply。 Server會把結果寫入這個參數。
第四個參數, 0。 這個參數只有兩個可選值:0和IBinder.FLAG_ONEWAY。
0表示這是一個雙向的IPC調用, 也就是Client向Server發起請求後, Server也會答覆Client。 IBinder.FLAG_ONEWA表示這是一個單向IPC調用, 也就是Client向Server發起請求後, 會直接返回, 不接受Server的答覆。
Server處理Client請求Client通過transact請求Server之後, Server可以在onTransact接收到Client的請求。
從data中讀出資料, 然後將結果寫入reply中。 整個過程就這樣。
運行先後安裝Server和Client程式, Client中就可以看到結果。
Demo項目代碼:
原理分析所謂原理分析就是追本溯源, 接下來我們看一下Client的請求是如何一步步到達Server的。
## IBinder
回到Client調用Server的代碼:
bindService(intent, new ServiceConnection{ @Override public void onServiceConnected(ComponentName name, IBinder service) { ... } @Override public void onServiceDisconnected(ComponentName name) { }}, Context.BIND_AUTO_CREATE);關鍵在於這個IBinder, Client是通過IBinder.transact將請求發給Server的。
這裡的IBinder實際上是個BinderProxy物件。 (我怎麼知道的?打斷點, 打日誌啊。 。 。 )
BinderProxy處於{framework}/core/java/android/os/Binder.java中。
final class BinderProxy implements IBinder { private long mObject; public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { ... return transactNative(code, data, reply, flags); } public native boolean transactNative(int code, Parcel data, Parcel reply, int flags) throws RemoteException; ...}Client調用BindProxy類的transact方法, 實際邏輯還是交給transactNative方法處理的。
接下來找到transactNative的代碼。
代碼在{framework}/core/jni/android_util_Binder.cpp中
static const JNINativeMethod gBinderProxyMethods = { ... {"transactNative", "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact} ...};可以看的transactNative是動態註冊的。 找到android_os_BinderProxy_transact方法, 看看它的代碼。
JNI方法註冊分為靜態註冊和動態註冊, 感興趣的朋友可以自行搜索瞭解。
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj, jint code, jobject dataObj, jobject replyObj, jint flags){ IBinder* target = (IBinder*) env->GetLongField(obj, gBinderProxyOffsets.mObject); status_t err = target->transact(code, *data, reply, flags); if (err == NO_ERROR) { return JNI_TRUE; } else if (err == UNKNOWN_TRANSACTION) { return JNI_FALSE; }}可以看到, 裡面又調用了target的transact方法, 將請求發送出去。
target是通過反射獲取BinderProxy類的mObject物件得到的。
final class BinderProxy implements IBinder { private long mObject;}long是怎麼被強轉為IBinder的?
實際上這裡的long mObject保存的是IBinder的指標。 指標的大小和long的大小都是一樣的, 都是4個位元組。
而名為target的這個IBinder實際上就是Server中onBind返回的這個Binder:
public class ManualCalculatorService extends Service{ @Nullable @Override public IBinder onBind(Intent intent) { return new Binder { ... }; }}到這裡, 我們就差不多明白了。 BinderProxy之所以叫BinderProxy, 它代理的就是Server中onBind返回的Binder。
而Client經過一層層的調用, 最終調用了Server中返回的Binder物件的transact方法。 我們看一下這個方法:
public final boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { ... boolean r = onTransact(code, data, reply, flags); ... return r;}這個方法實際上調用了onTransact方法進行具體的邏輯處理。 這也是為什麼我們可以在onTransact中處理Client請求的原因。
結尾關於target是怎麼來的?
target是通過反射獲取BinderProxy類的mObject物件得到的。
mObject保存了server中IBinder的指針。
那麼這個指標又是哪裡來的?
這裡不得不提到另外一個類:ServiceManager
該類在{framework}/core/java/android/os/ServiceManager.java中
感興趣的朋友可以閱讀它的代碼。
這裡簡單說一下:ServiceManager通過map保存了Service和IBinder的關係。 也就是通過Service的名稱就可以獲取到這個Service的IBinder。