想要掌握一样东西,最好的方式就是阅读理解它的源码。想要掌握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

项目代码:

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通信的更多相关文章

  1. android binder机制详解

    摘要 Binder是android中一个很重要且很复杂的概念,它在系统的整体运作中发挥着极其重要的作用,不过本文并不打算从深层次分析Binder机制,有两点原因:1是目前网上已经有2篇很好的文章了,2 ...

  2. [转]android Intent机制详解

    转自:http://blog.csdn.net/t12x3456/article/details/7688154 1.什么是Intent Intent是一种运行时绑定(run-time binding ...

  3. android Intent机制详解

    http://www.oschina.net/question/565065_67909 http://www.cnblogs.com/hummersofdie/archive/2011/02/12/ ...

  4. Android Download机制详解(一)DocumentUI部分

    在Android中Google为我们集成了一套十分便利的Download机制,用来下载网络上的资源文件.以此省去了我们编写和维护大量与Download相关的代码. 组成 Android中Downloa ...

  5. android Handler机制详解

      简单运行图:    名词解析: Message(消息):定义了一个包含描述以及随意的数据对象可以被发送到Hanlder的消息,获得消息的最好方法是Message.obtain或者Handler.o ...

  6. JavaScript系列----AJAX机制详解以及跨域通信

    1.Ajax 1.1.Ajax简介 Ajax简介这一部分我们主要是谈一下ajax的起源,ajax是什么?因为这些是跟技术无关的.所以,大多细节都是一笔带过. Ajax的起源? Ajax一词源于2005 ...

  7. 【转】Android - Binder机制

    以下几篇文章是分析binder机制里讲得还算清楚的 目录 1. Android - Binder机制 - ServiceManager 2. Android - Binder机制 - 普通servic ...

  8. Android AIDL使用详解_Android IPC 机制详解

    一.概述 AIDL 意思即 Android Interface Definition Language,翻译过来就是Android接口定义语言,是用于定义服务器和客户端通信接口的一种描述语言,可以拿来 ...

  9. Android 的事件传递机制,详解

    Android 的事件传递机制,详解 前两天和一个朋友聊天的时候.然后说到事件传递机制.然后让我说的时候,忽然发现说的不是非常清楚,事实上Android 的事件传递机制也是知道一些,可是感觉自己知道的 ...

随机推荐

  1. C/C++输入两个任意日期求相隔天数

    将两个日期转换成与一个指定日期(例1970-01-01)之间的差然后计算 思路: 两个日期相隔天数的计算,首先可以将两个日期转换成time_t(从指定日期至1970年1月1日0时0分0秒相隔的秒数), ...

  2. Java IO学习笔记五

    管道流 管道流的主要作用是可以进行两个线程间的通讯,分为管道输出流(PipedOutputStream).管道输入流(PipedInputStream),如果想要进行管道输出,则必须要把输出流连在输入 ...

  3. JS性能优化之怎么加载JS文件

    IE8+等实行并行下载,各JS下载不受影响,但仍阻塞其他资源下载 如: 图片 所以首要规则就是:将JS放在body底部(推荐) 加载100kb的单个文件比4个25kb的文件快(减少外链文件数量)(脚本 ...

  4. 取消PHPCMS V9后台新版本升级提示信息

    方法非常简单,只要找到文件: phpcms/libs/classes/update.class.php 文件,修改第50行的代码(大概位置): function notice() { return $ ...

  5. 关于MATLAB处理大数据坐标文件2017622

    今天新提交了一次数据,总量达到10337个,本以为成绩会突飞猛进,没想到还是不如从前 但是已经找到人工鼠标轨迹的程序,有待完善,接下来兵分四路:找特征.决策树.完善人工轨迹程序,使其可以将生成的数据自 ...

  6. 不带插件 ,自己写js,实现批量上传文件及进度显示

    今天接受项目中要完成文件批量上传文件而且还要显示上传进度,一开始觉得这个应该不是很麻烦,当我在做的时候遇到了很多问题,很头疼啊. 不过看了别人写的代码,自己也测试过,发现网上好多都存在一些问题,并不是 ...

  7. Example010实现浏览器兼容改内容的函数,自写

    <!-- 实例010实现浏览器兼容改内容的函数 --> <!DOCTYPE html> <html lang="en"> <head> ...

  8. 短信发送接口被恶意访问的网络攻击事件(四)完结篇--搭建WAF清理战场

    前言 短信发送接口被恶意访问的网络攻击事件(一)紧张的遭遇战险胜 短信发送接口被恶意访问的网络攻击事件(二)肉搏战-阻止恶意请求 短信发送接口被恶意访问的网络攻击事件(三)定位恶意IP的日志分析脚本 ...

  9. Agile&DevOps究竟谁是魔法棒

    天下没有神奇的配方 很抱歉,文章的开头我就要说出这个残酷的事实 - 世界上没有任何工具可以魔法般的让你实现敏捷,精益,DevOps.如果只是依赖了工具的自动化,实现了自动化Dev或者Ops,那么别忘了 ...

  10. maven自定义jar到本地仓库

    Apache Maven为项目构建提供了绝佳的解决方案,其本地仓库中缓存了远程代理仓库或中央仓库中的资源,从而提高网络资源使用效率,很好很强大!  但是并非所有资源都可以根据GroupId.Artif ...