本文作者
作者:小鱼人爱编程
链接:
https://juejin.cn/post/7157710923547803655
本文由作者授权发布。
Binder机制可谓是Android 知识体系里的重中之重,作为偏底层的基础组件,平时我们很少关注它,而它却是无处不在,也是Android 面试易考察的点之一。网上很多文章,要么知识点比较陈旧,要么源码贴一堆,要么没有成体系的分析,导致读者一知半解,似是而非。
本篇将从流程上将Binder通信过一遍,尽量多用图展示。
Binder的作用 进程与Binder驱动如何通信 ServiceManager进程的作用 进程添加服务到ServiceManager的流程 进程从ServiceManager获取服务的流程 Binder服务端数据接收 Binder 通信全流程图
我们知道,对象调用本身就是地址空间的访问。
如上,进程之间各自访问各自的内存地址,它们之间无法直接访问对方的地址,也就是说微信不能直接调用支付宝提供的接口。而内核具有访问其它进程地址空间的权限,因此微信可以将消息发送给内核,让内核帮忙转发给支付宝,这种方式叫做:存储/转发方式。
内核提供提供一系列的系统调用接口给用户进程使用,当用户进程想要访问内核时,只需要调用对应的接口,此时代码就会从用户空间切换到内核空间执行。
常见的系统调用函数如:open/read/write/ioctl/close/mmap/fork 等。
与Binder驱动通信分两步:
打开Binder驱动:open("/dev/binder", O_RDWR | O_CLOEXEC) 通过ioctl 与Binder驱动进行数据通信:ioctl(mDriverFD, BINDER_WRITE_READ, &bwr)
bwr 为读写数据结构
为方便起见,ServiceManager简称SM。
Binder 设计为C/S架构,C为Client(客户端),S为Server(服务端),Server端提供接口(服务)给Client端使用,而这个服务是以Binder引用的形式提供的。
由之前的知识可知,C和S是不同的进程,那么C如何拿到S的Binder引用呢?
你可能会说,当然是SM了,S先将Binder引用存放在SM里,当C需要的时候向SM查询即可。
这么看似乎讲得通了,那问题又来了,SM也是一个单独的进程,那S、C如何与SM进行通信呢?这就陷入了先有鸡还是先有蛋的死循环了。
实际上C、S、SM之间都是依靠Binder通信,只是SM作为特殊的Binder(handle=0)提前放入了Binder驱动里,当C、S想要获取SM的Binder引用,只需要获取handle=0的Binder即可。
这么说没有太直观的印象,我们一步步剖析。
其它进程添加服务到SM里。 其它进程向SM查询服务。
当SM收到查询服务的指令后,从Binder驱动里取出name,并找到链表里相同的name,找到后取出handle,最后写入到Binder驱动。
其它进程找到SM
先找到ServiceManager。 往ServiceManager里添加服务。
ProcessState里维护了一个单例,每个进程只有一个ProcessState对象,创建ProcessState时候就会去打开Binder驱动,同时会设置Binder线程池里线程个数等其它参数。 Native层构造BpBinder(handle=0表示该BpBinder是ServiceManager在客户端的引用),再构造BinderProxyNativeData持有BpBinder。 构造BinderProxy对象并持有BinderProxyNativeData,也就是间接持有BpBinder。 最后构造了ServiceManagerProxy对象,它实现了IServiceManager接口,它的成员变量mRemote指向了BinderProxy。
#ServiceManagerNative.ServiceManagerProxy
public void addService(String name, IBinder service, boolean allowIsolated, int dumpPriority)
throws RemoteException {
//构造Parcel
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IServiceManager.descriptor);
data.writeString(name);
//写入Binder
data.writeStrongBinder(service);
data.writeInt(allowIsolated ? 1 : 0);
data.writeInt(dumpPriority);
//通过BinderProxy发送
mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);
reply.recycle();
data.recycle();
}
服务的名字。 ServiceManager的handle。 BBinder对象指针。
Parcel.writeStrongBinder(Binder)
Binder驱动记录了BBinder的地址,当有消息过来时通过找到BBinder对象进而找到Java层的Binder对象,最终调用Binder.onTransact()。
private void vibrate() {
//获取振动服务
Vibrator vibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
//开始振动
vibrator.vibrate(1000);
}
#ServiceManagerNative.ServiceManagerProxy
public IBinder getService(String name) throws RemoteException {
//构造Parcel
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IServiceManager.descriptor);
//写入名字
data.writeString(name);
//通过BinderProxy发送
mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
IBinder binder = reply.readStrongBinder();
reply.recycle();
data.recycle();
return binder;
}
服务的名字 ServiceManager的handle
mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
IBinder binder = reply.readStrongBinder();
微信进程拿到振动服务(在system_server进程里)的Binder(BinderProxy)后,就可以调用振动方法了,而后指令发送给驱动,驱动通过振动服务的handle找到对应的服务BBinder指针,从而调用服务的接收方法。
微信进程调用BpBinder.transact()将handle和数据写入Binder驱动。 Binder驱动根据handle找到system_server进程。 system_server进程从驱动拿到数据,并取出BBinder指针,最终调用到system_server进程Java层的Binder.onTransact()。
如此一来,微信成功调用了振动服务,也就是说一次Client到Server端的通信就完成了。
纵观Binder机制设计,最核心的点是handle。
通过handle构造Client端的BpBinder(Native层),与此对应的是Java层的BinderProxy。 通过handle,驱动找到Server端进程,进而调用BBinder(Native层),与此对应的是Java层的Binder。 通过handle的一系列中转,Client.transact()成功调用了Server.onTransact(),一次Binder通信就过程就完成了。
本文基于Android 10
限于篇幅并没有一步步列出源码,对源码细节有疑惑之处欢迎留言讨论。
最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!
推荐阅读:
扫一扫 关注我的公众号
如果你想要跟大家分享你的文章,欢迎投稿~
┏(^0^)┛明天见!