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
项目代码:
https://github.com/a3349384/IPCDemo
原理分析
所谓原理分析就是追本溯源,接下来我们看一下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。
参考链接:https://www.zhoumingyao.cn/Android笔记/Android-Binder机制详解:手写IPC通信/
Android Binder机制详解:手写IPC通信的更多相关文章
- android binder机制详解
摘要 Binder是android中一个很重要且很复杂的概念,它在系统的整体运作中发挥着极其重要的作用,不过本文并不打算从深层次分析Binder机制,有两点原因:1是目前网上已经有2篇很好的文章了,2 ...
- [转]android Intent机制详解
转自:http://blog.csdn.net/t12x3456/article/details/7688154 1.什么是Intent Intent是一种运行时绑定(run-time binding ...
- android Intent机制详解
http://www.oschina.net/question/565065_67909 http://www.cnblogs.com/hummersofdie/archive/2011/02/12/ ...
- Android Download机制详解(一)DocumentUI部分
在Android中Google为我们集成了一套十分便利的Download机制,用来下载网络上的资源文件.以此省去了我们编写和维护大量与Download相关的代码. 组成 Android中Downloa ...
- android Handler机制详解
简单运行图: 名词解析: Message(消息):定义了一个包含描述以及随意的数据对象可以被发送到Hanlder的消息,获得消息的最好方法是Message.obtain或者Handler.o ...
- JavaScript系列----AJAX机制详解以及跨域通信
1.Ajax 1.1.Ajax简介 Ajax简介这一部分我们主要是谈一下ajax的起源,ajax是什么?因为这些是跟技术无关的.所以,大多细节都是一笔带过. Ajax的起源? Ajax一词源于2005 ...
- 【转】Android - Binder机制
以下几篇文章是分析binder机制里讲得还算清楚的 目录 1. Android - Binder机制 - ServiceManager 2. Android - Binder机制 - 普通servic ...
- Android AIDL使用详解_Android IPC 机制详解
一.概述 AIDL 意思即 Android Interface Definition Language,翻译过来就是Android接口定义语言,是用于定义服务器和客户端通信接口的一种描述语言,可以拿来 ...
- Android 的事件传递机制,详解
Android 的事件传递机制,详解 前两天和一个朋友聊天的时候.然后说到事件传递机制.然后让我说的时候,忽然发现说的不是非常清楚,事实上Android 的事件传递机制也是知道一些,可是感觉自己知道的 ...
随机推荐
- js___原生js轮播
原生js轮播 作为一名前端工程师,手写轮播图应该是最基本掌握的技能,以下是我自己原生js写的轮播,欢迎指点批评: 首先css代码 a{text-decoration:none;color:#3DBBF ...
- BarTender 通过ZPL命令操作打印机打印条码, 操作RFID标签
注: 由于工作需要, 也是第一次接触到打印机的相关内容, 凑巧, 通过找了很多资料和帮助后, 也顺利的解决了打印标签的问题 (标签的表面信息[二维码,条形码, 文字] 和 RFID标签的EPC写 ...
- 批量自动更新SVN版本库 - Windows
开发过程中每天都要从SVN代码库里一个一个的update各个项目代码,不仅效率实在是低,也不符合程序员的"懒"精神,由于是在Windows环境做开发,自然就想到了使用bat来实现自 ...
- python+NLTK 自然语言学习处理二:文本
在前面讲nltk安装的时候,我们下载了很多的文本.总共有9个文本.那么如何找到这些文本呢: text1: Moby Dick by Herman Melville 1851 text2: Sense ...
- java 得到uuid并处理
java 得到uuid String s = UUID.randomUUID().toString(); //去掉“-”符号 return s.substring(0,8)+s.substring(9 ...
- juggle添加c#版本
此前做过一个c++版的网络层dsl:http://www.cnblogs.com/qianqians/p/4255034.html 现在给这个dsl加入c#的支持,并且对代码的结构做了优化,将语法解析 ...
- 一个用 js 实现点阵图的编辑器演示
这是个客户的需求,具体大概是可以在一个 24*8 的点阵图上自由绘制图形,然后数据的存储是按列依次记录,用0和1分别表示是否选中,最终串成一个字符串. 整体需求难度并不复杂,所以在写demo的时候就尽 ...
- 推荐一个基于Vue2.0的的一款移动端开发的UI框架,特别好用。。。
一丶YDUI 一只注重审美,且性能高效的移动端&微信UI. 下面为地址自己研究去吧! 我的项目正在用,以前用的Mint-ui但是现在感觉还是这个好一点,官方给出的解释很清楚,很实用. 官方地址 ...
- eclipse从SVN检出项目之后,项目出错
今天公司把我分配到另一个项目组工作,然后下午使用SVN检出项目,出了问题 1.从SVN检出项目之后,要导入jar包.结果右键项目找不到Build Path,问了大牛才知道是这里的问题,一共四个步骤解决 ...
- 如何编写高效的SQL
编写高效的sql可以给用户带来好的体验,需要开发这没有意识到这一点因为他们不关心也不知道怎么做.高效的sql可以给用户节省很多时间,这样就不需要dba来找上门,也不需要搭建RCA和性能调优. 性能不好 ...