安卓Binder机制
来源:互联网 发布:淘宝店铺保证金怎么交 编辑:程序博客网 时间:2024/06/07 00:54
网上关于Binder的文章有很多,不少大牛高手围绕Binder施展了十八般武艺,想要将它解剖干净展示给大家看,文章的水平都很高,但可惜的是,都有点对开发者不友好,为什么?因为它们都是down-to-top,而不是top-to-down。作为应用开发者,我们肯定更喜欢top-to-down的讲解。本文则是一种尝试,可把它当作Weishu的这篇文章的上篇来阅读。声明:本文属作者原创,欢迎随意转载。但请不要修改文章内容并在开头注明本人简书账号milter和原文链接。
一、确定问题
本文围绕着这样一个问题展开:如何从进程A传两个整数给进程B,进程B把两个数相加后返回结果给进程A。
二、Binder机制框架概览
Android给我们提供了跨进程通信的一揽子解决方案。下面我们从总体上看一看这个方案是怎样设计的:进程A通过bindService
方法去绑定在进程B中注册的一个service
,系统收到进程A的bindService
请求后,会调用进程B中相应service
的onBind
方法,该方法返回一个特殊对象,系统会接收到这个特殊对象,然后为这个特殊对象生成一个代理对象,再将这个代理对象返回给进程A,进程A在ServiceConnection
回调的onServiceConnected
方法中接收该代理对象,依靠这个代理对象的帮助,就可以解决我们的问题啦。
三、分步骤分析
step 1: 进程B创建Binder 对象
为进程B实现一个特殊的对象,就是前面提到的service
的onBind
方法要返回的对象。这个对象有两个特性:
- 一个是具有完成特定任务的能力(在我们的问题中,就是将两个整数相加并返回结果的能力)
- 一个是被跨进程传输的能力。
什么样的对象具有这样的能力呢?答案是Binder
类的对象。下面我们分析一下Binder
是怎样拥有这两个能力的。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 { ......//Binder的一个内部类,暂时不用管,后面会讲。 }}
Binder具有被跨进程传输的能力是因为它实现了IBinder
接口。系统会为每个实现了该接口的对象提供跨进程传输,这是系统给我们的一个很大的福利。
Binder具有的完成特定任务的能力是通过它的attachInterface
方法获得的,我们可以简单理解为该方法会将(descriptor,plus
)作为(key,value
)对存入Binder
对象中的一个Map<String,IInterface>
对象中,Binder
对象可通过attachInterface
方法持有一个IInterface
对象(即plus
)的引用,并依靠它获得完成特定任务的能力。queryLocalInterface
方法可以认为是根据key
值(即参数 descriptor
)查找相应的IInterface
对象。onTransact
方法暂时不用管,后面会讲到。
好的,现在我们来实现IInterface
和Binder
对象,概略代码如下:
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
方法获取这个binder
的plus
对象,利用该对象的加法功能进行加法计算。可结果呢?
首先,binderproxy.queryLocalInterface("PLUS TWO INT")
调用是合法的,因为queryLocalInterface
方法是IBinder
中的方法,而BinderProxy
和Binder
都实现了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.ADD
。Stub.ADD
其实可以是任何整数值的,我们选择1纯属为了简单。
step 4: 进程B收到并处理进程A的请求
binderproxy.transact
调用发生后,会引起系统的注意,系统意识到binderproxy
想找它的真身binder
对象执行一个操作了(看!系统其实一直存着binder
和binderproxy
的对应关系呢!)。于是系统将这个请求中的数据转发给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();
-----2016.07.19补充----
好了,借助Android给我们提供的Binder
机制,我们成功解决了文章开头提出的问题。但我们可以做得更好一点。比如,我们可以将下面这段代码封装一下。
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暂时不管它 reply.readException(); _result = reply.readInt();
具体封装方法是建一个PlusProxy类,如下:
public class PlusProxy implements IPlus { private IBinder binderproxy ; public PlusProxy(IBinder binderproxy){ this.binderproxy = binderproxy ; } public int add (int a ,int b ){ android.os.Parcel data = android.os.Parcel.obtain(); android.os.Parcel reply = android.os.Parcel.obtain(); data.writeInterfaceToken("PLUS TWO INT"); data.writeInt(a); data.writeInt(b); binderproxy.transact(1, data, reply, 0); int _result; reply.readException(); _result = reply.readInt(); return _result ; }}
有了PlusProxy
类,进程A就可以利用收到的binderproxy
对象创建一个PlusProxy
对象plusproxy
,然后就可以将它当作plus
使用了。我们可以非常愉悦地调用plusproxy.add(2,3)
得到2+3
的和。
还能做得更好一点吗?答案是yes。
我们可以在Stub类中增加一个静态辅助方法public static IPlus asInterface(Ibinder)
,让进程A收到binderproxy
对象时调用Stub.asInterface(binderproxy);
该方法负责利用binderproxy
对象构造一个PlusProxy
对象,然后作为IPlus
返回给我们。
总结:进程A向进程B申请Stub
类(继承自Binder
)的对象binder
,想从binder
中获得IPlus
接口类型的对象plus,以便利用plus
中的add
方法做加法计算。当进程A发现收到的不是binder
真身而是它的代理binderproxy
时,它在自己进程内构建了一个plus
的代理对象plusproxy
(类型为PlusProxy
,实现了与plus
相同的的IPlus
接口),该代理对象的add
方法利用binderproxy
去向binder
申请加法计算,并把结果返回。这样,从外表上来看,进程A获得了进程B中的plus
对象,这就是Binder
跨进程通信的本质。
如果你理解了本文的思想,再去看aidl生成的代码,就一定会觉得很简单的。
如果通过本文,让你对Binder
有了更深的认识,消解了你心头萦绕许久的困惑。点赞吧!
作者:milter
链接:http://www.jianshu.com/p/1eff5a13000d
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
- 安卓Binder机制
- 安卓IPC机制之Binder详解
- 安卓复习之旅—Binder工作机制
- 安卓Binder
- 安卓Binder解析
- 安卓binder机制executeCommand(...)方法sp<BBinder> b((BBinder*)tr.cookie);分析
- Binder机制
- Binder机制
- Binder机制
- binder机制
- binder机制
- Binder机制
- Binder机制
- Binder机制
- Binder机制
- Binder机制
- Binder机制
- Binder机制
- Lightoj1068——Investigation(数位dp)
- 1-3 数组的反序输出
- C++的multimap自定义key值
- base64在html的用法
- js多数组进行排列组合
- 安卓Binder机制
- 毕业5年决定人的一生
- <![CDATA[ ]]>
- 基于51单片机的GPS公交自动报站系统
- MemCache超详细解读
- 需要的依赖
- recyclerview展示数据(MVP)
- DB2 SQL Error: SQLCODE=-668, SQLSTATE=57016 解决方案
- springmvc双模板引擎