當前位置:
首頁 > 知識 > Android Binder機制詳解:手寫IPC通信

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

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


簡介

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

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


開始

新建測試工程

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

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

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

  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。

<service android:name=".ManualCalculatorService"
android:exported="true"
android:process=":manualremote"/>

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
@Override
public 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。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 達人科技 的精彩文章:

九大排序演算法整合
[Android FrameWork 6.0源碼學習] View的重繪過程之Layout
mysql 5.7 root密碼重置(centos 7)
node調用phantomjs-node爬取複雜頁面
關於canvas畫布使用fillRect()時高度出現雙倍效果解決辦法

TAG:達人科技 |

您可能感興趣

蘋果MacBook Air/Mac Mini/iPad Pro新品詳解
天維信通詳解AWS Direct Connect Gateway服務
Paint API之 Xfermode與PorterDuff詳解
iOS Airplay Screen Mirroring 同屏技術詳解
CodeWarrior IDE使用Tips-使用burner將elf文件轉換生成HEX和BIN文件的方法和步驟詳解
聚合查詢慢——詳解Global Ordinals與High Cardinality
FPGA與ASIC的完美結合,Achronix Speedster 7t系列詳解
MyBatis 配置 typeHandlers 詳解
Spring Cloud限流詳解
一文詳解如何使用Python和Keras構建屬於你的「AlphaZero AI」
Tensorboard 詳解
一文詳解如何使用Python和Keras構建屬於你的AlphaZero AI
Spring Security 5.0 的 DelegatingPasswordEncoder 詳解
Spring MVC之DispatcherServlet初始化詳解
Classical CNN models:LeNet-5 模型結構詳解
使用Wireshark詳解TCP協議
俄羅斯小丑KonstantinChaykinJoker腕錶詳解評測
AlphaGo之父DeepMind再出神作,PrediNet原理詳解
Spring Cloud中的Eureka服務註冊與發現詳解
高能細節詳解!TAKAHIROMIYASHITA TheSoloist x Converse聯名系列!