您的位置:首頁>正文

Android Binder機制詳解:手寫IPC通信

想要掌握一樣東西, 最好的方式就是閱讀理解它的源碼。 想要掌握Android Binder, 最好的方式就是寫一個AIDL檔, 然後查看其生成的代碼。 本文的思路也是來自於此。

簡介

Binder是Android常用的一種進程間通信方式。 當然, 不使用Binder, 你還可以使用Socket甚至檔來進行通信。

通常Android上的進程間通信, 指的就是遠端Service的調用。

開始新建測試工程

打開Android Studio新建IPCClient和IPCServer兩個app工程。

假設我們要做這樣一件事情:

Client向Server發起一個請求:請告訴我1+2等於多少

Server將答案返回給Client

創建遠端Service

IPCServer新建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調用, 上面兩個缺一不可。

創建Client

Client調用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調用Server

Client可以通過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的請求。

@Nullable@Overridepublic IBinder onBind(Intent intent){ return new Binder { @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { switch (code) { case 1000: { int num1 = data.readInt; int num2 = data.readInt; reply.writeInt(num1 + num2); return true; } } return super.onTransact(code, data, reply, flags); } };}

從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。

同類文章
Next Article
喜欢就按个赞吧!!!
点击关闭提示