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。
<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聯名系列!