Binder 架构设计

Binder 被设计出来是解决 Android IPC(进程间通信) 问题的。Binder 将两个进程间交互的理解为 Client 向 Server 进行通信。

如下:binder总体架构图

如上图所示,Binder 架构分为 Client、Server、Service Manager 和 Binder Driver。

  • Client: 服务调用者,一般就是我们应用开发者,通过调用诸如List<PackageInfo> packs = getActivity().getPackageManager().getInstalledPackages(0); 这样的代码,来向 ServerManager 请求 Package 服务。
  • Server: 服务提供者,这里面会有许多我们常用的服务,例如 ActivityService 、 WindowMananger, 这些系统服务提供的功能,是的我们能够使用 Wifi,Display等等设备,从而完成我们的需求。
  • Service Manager: 这里是类似于前文中的DNS,绝大多数的服务都是通过 Service Manager来获取,通过这个 DNS 来屏蔽掉 对其他Server的直接操作。
  • Binder Driver: 底层的支持逻辑,在这里承担路由的工作,不论风雨,使命必达,即使对面的server挂掉了,也会给你相应的死亡通知单 (Death Notification)

总结起来说,应用程序(Client) 首先向 Service Manager 发送请求 WindowManager 的服务,Service Manager 查看已经注册在里面的服务的列表,找到相应的服务后,通过 Binder kernel 将其中的 Binder 对象返回给客户端,从而完成对服务的请求。

Binder Driver 是怎样充当路由角色的?

对于有网络编程经验的人来说,Socket 是很常用的概念。在Linux系统中,一切都被认为是文件,网络流也是文件,同样 Socket 也是文件,遵循着 open - write / read - close的模式,Binder Framework在设计的时候,也同样设计了类似的概念。

而在 Binder Framework 中 Binder 充当了 Socket 的角色,在不同的进程里面穿梭,提供了通信的基础。对Binder而言,Binder可以看成Server提供的实现某个特定服务的访问接入点, Client通过这个『地址』向Server发送请求来使用该服务;对Client而言,Binder可以看成是通向Server的管道入口,要想和某个Server通信首先必须建立这个管道并获得管道入口。我们知道如果要访问一个对象的话,需要拿到这个对象的引用地址,我们可以这么认为 Binder 就是远程对象的一个地址,通过这个 Binder 就能轻松地拿到远程对象的控制权,也可以说 Binder 是句柄,可能符合现在的场景。

而让 Binder 起到上诉神奇作用的就是 Binder Driver。Binder Driver 在这里的作用就是前面提及的路由器,它工作在内核态,通过一系列 open() , mmap()ioctl() , poll() 等操作,指定了一系列的协议,实现了 Binder 在不同进程之间的传递工作,这里就不再详细阐述了,有兴趣的同学可以自行查看相关文档。

Service Manager 怎么当DNS的?

根据前文的描述,Service Manager是将相应的服务名字转换成具体的引用,也就是说使得 client 能够通过 bidner 名字来从 Server 中拿到对 binder 实体的引用。这里唯一需要特别说明的地方在于,Service Manager 的特殊性。我们知道 Service Manager 是一个进程,其他 Server 也是另一个进程,他们之间是如何进行通信的了?在没有其他中间服务进程的参与下,Service Manager 与 其他进程如何凭空通信?

这就是先有鸡,还是先有蛋的问题。答案是先有鸡,也就是说 Service Manager 首先就被创建了,并被赋予了一个特殊的句柄,这个句柄就是 0 。换而言之,其他 Server 进程都可以通过这个 0句柄 与 Service Manager 进行通信,在整个系统启动时,其他 Server 进程都向这个 0句柄 进行注册,从而使得客户端进程在需要调用服务时,能够通过这个 Service Manager 查询到相应的服务进程。

如下:binder framework 工作原理图

理解Aidl中Stub和Stub.Proxy

aidl生成的java代码中,Stub类是继承于Binder类的,也就是说Stub实例就是Binder实例。

服务端一般会实例化一个Binder对象,例如:

public class AIDLService extends Service {
private static final String TAG = "AIDLService";
IPerson.Stub stub = new IPerson.Stub() {
@Override
public String greet(String someone) throws RemoteException {
Log.i(TAG, "greet() called");
return "hello, " + someone;
}
};
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind() called");
return stub;
}
...
}

客户端中在Service绑定的时候可以获取到这个Stub(Binder),如:

    private IPerson person;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i("ServiceConnection", "onServiceConnected() called");
person = IPerson.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
//This is called when the connection with the service has been unexpectedly disconnected,
//that is, its process crashed. Because it is running in our same process, we should never see this happen.
Log.i("ServiceConnection", "onServiceDisconnected() called");
}
};

像上面一样,在连接Service的时候,服务端的Stub(Binder)以参数的形式传过来了–IBinder service,然后我们通过asInterface()方法获取它的实例对象。

aidl文件自动生成的java类中可以看到asInterface()这个接口的实现,大概的意思就是: 
如果客户端和服务端在同一个进程下,那么asInterface()将返回Stub对象本身,否则返回Stub.Proxy对象。

也就是说asInterface()返回的对象有两种可能(实际上有三种,还有一种是null),Stub和Stub.Proxy。它们有什么区别呢?

  1. 如果在同一个进程下的话,那么asInterface()将返回服务端的Stub对象本身,因为此时根本不需要跨进称通信,那么直接调用Stub对象的接口就可以了,返回的实现就是服务端的Stub实现,也就是根本没有跨进程通信;

  2. 如果不是同一个进程,那么asInterface()返回是Stub.Proxy对象,该对象持有着远程的Binder引用,因为现在需要跨进程通信,所以如果调用Stub.Proxy的接口的话,那么它们都将是IPC调用,它会通过调用transact方法去与服务端通信。

以上就是两者的区别。

Stub是服务端实现的存根,而Proxy则是Stub的代理。

public interface IPerson extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements IPerson {
public Stub(){
this.attachInterface(this, DESCRIPTOR);
}
...
public static IPerson asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
   //mDescriptor的初始化在attachInterface()过程中赋值
android.os.IInterface iin = (android.os.IInterface) obj.queryLocalInterface(DESCRIPTOR); //查询本地
if (((iin != null) && (iin instanceof IPerson))) {
return ((IPerson) iin);
}
return new IPerson.Stub.Proxy(obj);
}
...   
@Override
public boolean onTransact(int code, android.os.Parcel data,
android.os.Parcel reply, int flags)
throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_greet: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _result = this.greet(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements IPerson {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public java.lang.String greet(java.lang.String someone)
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(someone);
mRemote.transact(Stub.TRANSACTION_greet, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
    static final int TRANSACTION_greet = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); //方法用数字表示
}
}

Binder机制框架概览

如何从进程A传两个整数给进程B,进程B把两个数相加后返回结果给进程A。

下面我们从总体上看一看这个方案是怎样设计的:
进程A通过bindService方法去绑定在进程B中注册的一个service,系统收到进程A的bindService请求后,会调用进程B中相应serviceonBind方法,该方法返回一个特殊对象,系统会接收到这个特殊对象,然后为这个特殊对象生成一个代理对象,再将这个代理对象返回给进程A,进程A在ServiceConnection回调的onServiceConnected方法中接收该代理对象,依靠这个代理对象的帮助,就可以解决我们的问题啦。

总体流程如下图:

step 1: 进程B创建Binder 对象

为进程B实现一个特殊的对象,就是前面提到的serviceonBind方法要返回的对象。这个对象有两个特性:

  • 一个是具有完成特定任务的能力(在我们的问题中,就是将两个整数相加并返回结果的能力)
  • 一个是被跨进程传输的能力。

什么样的对象具有这样的能力呢?答案是Binder类的对象。下面我们分析一下Binder是怎样拥有这两个能力的。
Binder中有如下关键方法:

public class AIDLService extends Service {
private static final String TAG = "AIDLService";
IPerson.Stub stub = new IPerson.Stub() {
@Override
public String greet(String someone) throws RemoteException {
Log.i(TAG, "greet() called");
return "hello, " + someone;
}
};
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind() called");
return stub;
}
...
}

Binder具有被跨进程传输的能力是因为它实现了IBinder接口。系统会为每个实现了该接口的对象提供跨进程传输,这是系统给我们的一个很大的福利。
Binder具有的完成特定任务的能力是通过它的attachInterface方法获得的,我们可以简单理解为该方法会将(descriptor,plus)作为(key,value)对存入Binder对象中的一个Map<String,IInterface>对象中,Binder对象可通过attachInterface方法持有一个IInterface对象(即plus)的引用,并依靠它获得完成特定任务的能力。queryLocalInterface方法可以认为是根据key值(即参数 descriptor)查找相应的IInterface对象。onTransact方法暂时不用管,后面会讲到。

好的,现在我们来实现IInterfaceBinder对象,概略代码如下:

public interface IPlus extends IInterface {
public int add(int a,int b);
} public class Stub extends Binder {
@Override
boolean onTransact(int code, Parcel data, Parcel reply, int flags){
......//这里我们覆写了onTransact方法,暂时不用管,后面会讲解。
}
......
}
IInterface plus = new IPlus(){//匿名内部类
public int add(int a,int b){//定制我们自己的相加方法
return a+b;
}
public IBinder asBinder(){ //实现IInterface中唯一的方法,
return null ;
}
};
Binder binder = new Stub();
binder.attachIInterface(plus,"PLUS TWO INT");

step 2: 进程A接收进程B的Binder对象

好了,现在我们有了这个特殊的对象binder,可以在进程B的service中的onBind方法将它返回了,即return binder ;
下面就是见证奇迹的时候。系统会首先收到这个binder对象,然后,它会生成一个BinderProxy(就是前面提到的Binder 的内部类)类的对象,姑且称之为binderproxy,然后将该对象返回给进程A,现在进程A终于在onServiceConnected方法中接收到了binderproxy对象(心情有木有小激动?)。为了下面讲解方便,再次贴出Binder类的概要信息。

public class Binder implement IBinder{
void attachInterface(IInterface plus, String descriptor)
IInterface queryLocalInterface(Stringdescriptor) //从IBinder中继承而来
boolean onTransact(int code, Parcel data, Parcel reply, int flags)//暂时不用管,后面会讲。
final class BinderProxy implements IBinder {
IInterface queryLocalInterface(Stringdescriptor) {
return null ;//注意这行代码!!
//下面会讲到。这行代码只是示例,不是源代码。
}
......
}
}

此时的进程A以为收到的是binder对象,它兴奋了,它迫不及待地要通过queryLocalInterface方法获取这个binderplus对象,利用该对象的加法功能进行加法计算。可结果呢?
首先,binderproxy.queryLocalInterface("PLUS TWO INT")调用是合法的,因为queryLocalInterface方法是IBinder中的方法,而BinderProxyBinder都实现了IBinder接口。但是,binderproxy对象显然没有plus对象,因为它根本就没有attachInterface方法(这是Binder才有滴)。所以,可想而知,进程A的binderproxy.queryLocalInterface("PLUS TWO INT")调用返回的将是一个null(参见上面的示例代码)。

step 3: 进程A利用进程B传过来的对象发起请求

进程A出离愤怒了,我要的是binder,我要的是它里面的plus来帮我完成加法运算,进程B竟然给我一个冒牌货binderproxy(显然,它冤枉了进程B,都是系统惹得祸)。
正在进程A气得头顶冒烟时,binderproxy对象说话了:“别生气进程A,我虽然只是binder对象的代理,但是,我也不是吃素的,你把你的数据(两个int)和你想进行的操作(plus.add)通过我的transact方法(这是在IBinder接口中定义的方法)交给我,我可以替你向binder对象请求你需要的功能,等binder对象把结果给我时,我再把结果交给你不就行了?”
于是,进程A通过binderproxy对象的transact方法,提交了请求。代码概略如下:

android.os.Parcel data = android.os.Parcel.obtain();
android.os.Parcel reply = android.os.Parcel.obtain();
int _result;
data.writeInterfaceToken("PLUS TWO INT");
data.writeInt(a);
data.writeInt(b);
binderproxy.transact(1, data, reply, 0);//为简单起见,最后一个0暂时不管它

简单解释一下上面代码。data是用来写进程A的数据的(即整数 a和b),reply是准备用来接收结果的。transact方法中的第一个参数是整数1,它是进程A与进程B的一个约定,1就代表想让进程B对进程A传入的数据执行加法操作。这个约定也可以定义在 Stub类中,如下所示:
public static final int ADD = 1;此时,我们可以将binderproxy.transact(1, data, reply, 0);中的1替换为Stub.ADDStub.ADD其实可以是任何整数值的,我们选择1纯属为了简单。

step 4: 进程B收到并处理进程A的请求

binderproxy.transact调用发生后,会引起系统的注意,系统意识到binderproxy想找它的真身binder对象执行一个操作了(看!系统其实一直存着binderbinderproxy的对应关系呢!)。于是系统将这个请求中的数据转发给binder对象,binder对象将会在onTransact中收到binderproxy传来的数据(Stub.ADD,data,reply,0),于是它从data中取出进程A传来的数据,又根据Stub.ADD确定进程A想让它执行加法操作,于是它就执行了加法操作,并把结果写回reply。代码概略如下:

public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
} //样板代码,不用管,下一行才是重点。
case Stub.ADD: {
data.enforceInterface("PLUS TWO INT");
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.queryLocalIInterface("PLUS TWO INT") .add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

简单解释一下以上代码。我们知道进程A写数据时写入了一个InterfaceToken,就是这行代码
data.writeInterfaceToken("PLUS TWO INT");
这个意思是说,让进程B在自己的binder对象中利用PLUS TWO INT调用queryLocalIInterface方法查找相应的IInterface对象,进程A要执行的操作就在该对象中,至此,我们很容易理解Stub.ADD就代表了plus中的add方法。这是一个二级查找过程,即通过PLUS TWO INT确定要plus来执行功能,通过Stub.ADD确定要执行plus中的add方法。

step 5: 进程A获取进程B返回的处理结果

进程B把结果写入reply后,进程A就可以从reply读取结果了。代码概略如下:

binderproxy.transact(Stub.ADD, data, reply, 0);
reply.readException();
_result = reply.readInt();

更深入的Binder原理参考

Android Bander设计与实现 - 设计篇

android Binder机制(一)架构设计的更多相关文章

  1. Android Binder机制彻底梳理二

    根据AIDL了解整体调用流程[重点分析AIDL流程]: 在上一次https://www.cnblogs.com/webor2006/p/11741743.html中我们已经对Android Binde ...

  2. 【转】Android - Binder机制

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

  3. Android Binder机制(一) Binder的设计和框架

    这是关于Android中Binder机制的一系列纯技术贴.花了一个多礼拜的时间,才终于将其整理完毕.行文于此,以做记录:也是将自己所得与大家分享.和以往一样,介绍Binder时,先讲解框架,然后再从设 ...

  4. Android binder机制---概述

    1.进程间通讯的原因 目前操作系统都使用虚拟存储技术,管理内存. 假设是32位机器,0-3G是用户空间,3-4G是系统使用.虚拟内存和逻辑内存都按4K分页.这样虚拟内存和逻辑内存就存在对应关系. 一个 ...

  5. ANDROID BINDER机制浅析

    Binder是Android上一种IPC机制,重要且较难理解.由于Linux上标准IPC在灵活和可靠性存在一定不足,Google基于OpenBinder的设计和构想实现了Binder. 本文只简单介绍 ...

  6. 理解 Android Binder 机制(一):驱动篇

    Binder的实现是比较复杂的,想要完全弄明白是怎么一回事,并不是一件容易的事情. 这里面牵涉到好几个层次,每一层都有一些模块和机制需要理解.这部分内容预计会分为三篇文章来讲解.本文是第一篇,首先会对 ...

  7. Android Binder机制介绍

    做过Android开发的同学可能有些体会,入门初期,工作内容主要是实现各式各样的UI界面,以及实现应用的业务逻辑.在这个阶段,我们会逐渐熟悉View系统,逐渐学会实现各种各样的界面以及动画效果.再往后 ...

  8. Android Binder机制彻底梳理一

    Binder架构图: 先来瞅一下它的整体架构图: 其中粉红部分是上层的Binder,而蓝色的则是下层的Binder,很显然上层的是依赖于下层的. 什么是Binder[有个大概了解]? 这里从几个层面来 ...

  9. 浅谈android binder机制

    binder机制 是谷歌优化在android上更适合终端的IPC(多进程通信方式),满足系统对通信方式,传输性能和安全性的要求. 特性: 1. 用驱动程序来推进进程间的通信.2. 通过共享内存来提高性 ...

随机推荐

  1. Django框架 之 view视图

    Django框架 之 view视图 浏览目录 概述 简单的视图 HttpRequest对象 CBV和FBV 给视图加装饰器 Request对象 Response对象 JsonResponse对象 Dj ...

  2. Paint类的介绍

    * Paint即画笔,在绘图过程中起到了极其重要的作用,画笔主要保存了颜色, * 样式等绘制信息,指定了如何绘制文本和图形,画笔对象有很多设置方法, * 大体上可以分为两类,一类与图形绘制相关,一类与 ...

  3. RGB565的转换

    RGB色彩模式也就是“红绿蓝”模式是一种颜色标准,是通过对红(R).绿(G).蓝(B)三种颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红.绿.蓝三个通道的颜色,这个标准几 ...

  4. easyUI 展开DataGrid里面的行显示详细信息

    http://blog.csdn.net/yanghongchang_/article/details/7854156原著 datagrid 可以改变它的view(视图)去显示不同的效果.使用详细视图 ...

  5. c#创建Table

    private void BindDatazhangting() { DataTable dt = new DataTable(); dt.Columns.Add("channel" ...

  6. C#非泛型集合和泛型集合

    第一  : ArrayList(非泛型集合)  与List(泛型集合) ArrayList 是数组的复杂版本.ArrayList 类提供在大多数 Collections 类中提供但不在 Array(数 ...

  7. SQLServer存储引擎——03.日志

    3. SQLServer存储引擎之日志篇 (3.1)日志结构 (3.1.1)物理日志 (0)物理日志即数据库的.ldf文件, 当然后缀名是可以自定义的,默认是.ldf (1)一个SqlServer数据 ...

  8. 动态绑数据(Repeater控件HeaderTemplate和ItemTemplate)

    前几天,Insus.NET有写了<动态绑数据(GridView控件Header和ItemTemplate)>http://www.cnblogs.com/insus/p/3303192.h ...

  9. ubuntu - 安装sqoop

    解析过程 参考:https://www.cnblogs.com/qingyunzong/p/8807252.html#_label4 一.下载 二.解压到指定目录 三.配置sqoop环境变量并生效 四 ...

  10. mysql 保存23:59:59 自动加一秒

    mysql保存系统传递的时间会诡异的增加一秒钟使用如下工具类: public static Date getEndOfDay(Date date) { Calendar calendarEnd = C ...