大家好,又见面了,我是你们的朋友全栈君。
Linux内核的基础知识
Binder通信机制
所以什么是Binder?
从Framework层看Binder原理
首先,Android利用Binder进行通信的话,肯定要首先获取Binder对象。
首先系统服务和我们自定义服务Binder对象的获取方式是不一样的,原因在于系统服务是在系统启动的时候就被注册到了ServiceManager,所以只需要通过ServiceManager.getService(String name)传入想要获得的系统服务的名字就能获得对应的Binder对象进行进程间通信了,系统已经将该Service所提供的服务全部写好了:系统启动,init进程会启动一个叫ServiceManager的进程,这个进程启动之后会做三件事请:第一通过open打开设备文件/dev/binder,将这个文件当中的内容通过mmap映射到本进程空间当中;第二是通过IO控制命令BINDER_SET_CONTEXT_MGR将当前进程注册到Binder驱动当中,然后Binder驱动就会为他在内核空间创建一个称为binder_context_mgr_node的节点,这个节点就是ServiceManager在内核空间Binder实体了,然后将它的句柄设置为0,它的创建时在系统启动的时候,这个节点在Binder驱动中是唯一的,所以也造成了ServiceManager区别于其他Server了,但是它仍然是运行在用户空间的;第三ServiceManager启动之后会无限循环等待Server和Client之间的进程间通信请求了;接着Zygote进程会孵化一个子进程SystemService,我们的大部分系统服务比如ActivityManagerService、WindowManagerService、MediaPlayService等等都是这个进程内的一个线程,这些服务都是会调用ServiceManager.addService方法注册添加到ServiceManager中的服务列表svlist中,这样系统服务就都注册到ServiceManager当中了。
ServiceManager是随着系统的启动而创建的,它不同于普通的Server,甚至可以理解为是普通Server的Server,系统会将所要提供的服务注册到它的服务列表中,服务列表中存储的就是服务名字和服务在Binder驱动中的句柄,这里的句柄可以理解为同一进程内部的引用,只不过现在是跨进程通信换了个名字而已,如果我们的应用想要使用某个系统服务的话,可以传入服务的名字调用ServiceManager.getService得到这个服务对应的Binder对象这种方式来进行获取,然后就可以使用这个Binder对象来进行相应操作了,具体来说这也是ContentProvider为我们封装的内容了。
Binder就是通过客户端/服务端模式实现的,关于Binder的实现,还需要区分几个概念:Server/Client/ServiceManager/Binder驱动,这个部分是Binder架构的核心,尤其是ServiceManager和Binder驱动,因为我们的应用程序安装到系统上的时候,都会被分配一个唯一的用户ID,它们是运行在独立进程当中的,Linux基于安全的考虑是不允许我们直接访问系统上服务的,因为它们属于不同的进程,而进程间的数据是不能共享的,那么要使用到这些系统服务就需要通过跨进程通信机制了,也就需要内核空间的参与,所以用到Binder驱动,它的作用相当于是桥梁。平常我们访问系统服务,比如媒体资源是使用ContentProvider,它的底层实现就是BInder,只是系统为我们做了封装。一句话总结下Client和Server通信时,ServiceManager相当于通讯录,而Binder驱动就是通信基站。
具体的通信过程是这样的:
Server和Binder驱动通信ServiceManager.addService(String name,IBinder service),传入的内容是Server的名字和Server在Binder驱动当中对应的对象:(1)Server首先将自己作为对象,并且附上一个句柄为0的值(用于访问ServiceManager),将这些内容封装成一个数据包,open有关Binder的设备文件/dev/binder,将封装好的这个数据包发送给Binder驱动;(2)Binder驱动在收到这个数据包之后,发现里面存在一个Server对象,首先会在Binder驱动自己内部新建该Server对应的Binder实体,并且赋予一个大于0的句柄,同时会将这个句柄也加入到数据包当中,接着会将数据包发送到句柄为0对应的对象,也就是ServiceManager了。(3)ServiceManager收到转发给自己的数据包之后,会查看其服务列表svlist中是否已经存在当前Server名字的服务,如果不存在的话,就会将当前服务+当前服务对应于Binder驱动中的句柄加入到列表当中。 这个过程,系统服务就注册到ServiceManager当中了。
Client和Binder驱动通信ServiceManager.getService(String name),返回对应的所请求Binder对象,传入的参数是将要请求的服务的名字:(1)Client首先会将要获取的服务的名字以及一个句柄为0的值(为了访问ServiceManager)封装成一个数据包,open有关Binder的设备文件/dev/binder,将该数据包发送给Binder驱动;(2)Binder驱动在收到数据包之后发现里面有句柄为0的信息,就将该数据包转发给ServiceManager来进行处理了;(3)ServiceManager在收到数据包之后根据服务名字查看自己服务列表svlist,找到之后会将对应的在Binder驱动当中的句柄信息也封装成一个数据包;(4)该数据包也会通过Binder驱动被发送给Client端;(5)Client端在收到数据包之后,就得到了自己所请求的服务在Binder驱动中的句柄,它会利用这个句柄信息在自己本地创建一个远程Server的代理,以后Client发消息都是发给这个代理的,随后的通信就都变成了代理通过Binder驱动与真正的Server进行交互,去完成跨进程通信。
这就是系统服务怎么注册到ServiceManager内,以及怎么获取服务对应于Binder驱动的句柄也就是Binder对象的过程。
而对于自定义的服务想要实现进程间通信的话,Client端和Server端都是需要我们自己实现的,其中Server端的实现是借助于Service来完成的,Android就提供AIDL帮助简化这个过程,当然就算不用AIDL也是可以实现跨进程通信的,只是序列化和反序列化数据的封装和相关顺序问题需要注意。使用AIDL只需要将Server进程想要提供给Client进程访问的方法定义在一个.aidl文件中就可,假设IStudentService.aidl,那么系统会为这个AIDL文件自动生成对应的IStudentService.java文件,它的基本结构是这样的:(1)首先这个接口会继承于android.os.IInterface接口;(2)会声明一个名为Stub的内部抽象类,继承自android.os.Binder,也就是说是一个Binder类,并且实现remote.IStudentService本身这个接口;(3)Stub类包含了一些方法和一个内部代理类Proxy:
package remote;
public interface IStudentService extends android.os.IInterface {
/**
* Default implementation for IStudentService.
*/
public static class Default implements remote.IStudentService {
@Override
public remote.Student getStudentById(int id) throws android.os.RemoteException {
return null;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements remote.IStudentService {
private static final java.lang.String DESCRIPTOR = "remote.IStudentService";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an remote.IStudentService interface,
* generating a proxy if needed.
* !!!!
* 用于将服务端的Binder对象转换成客户端所需要的AIDL接口类型对象,这种转换过程是区分进程的,
* 会根据传入的Binder对象来判断是跨进程通信还是进程内部通信:
* 如果是进程内部通信,返回的是IStudentService.Stub对象本身;
* 如果是跨进程通信,返回的就是remote.IStudentService.Stub.Proxy(obj)代理对象,
* 这个代理对象中的方法会通过调用transact方法来进行内核态的切换。
*/
public static remote.IStudentService asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof remote.IStudentService))) {
return ((remote.IStudentService) iin);
}
return new remote.IStudentService.Stub.Proxy(obj);
}
//返回当前Stub也就是Binder对象
@Override
public android.os.IBinder asBinder() {
return this;
}
/**
* 这个方法运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封
* 装后交由此方法处理。通过code确定客户端所请求的目标方法,接着从data中取出目标方法所需参数
* ,然后执行目标方法。目标方法执行完毕,向reply写入返回值。
* 而如果这个方法返回false,客户端的请求就会失败,所以也可以用这个特性来做权限验证。
*/
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getStudentById: {
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
remote.Student _result = this.getStudentById(_arg0);
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements remote.IStudentService {
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;
}
/**
* 内部调用transact方法发起RPC请求,进行远程过程调用,同时当前线程挂起;
* 然后服务端的onTransact方法会被调用,直到RPC过程返回后,当前线程继续执行。
*/
@Override
public remote.Student getStudentById(int id) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
remote.Student _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(id);
boolean _status = mRemote.transact(Stub.TRANSACTION_getStudentById, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getStudentById(id);
}
_reply.readException();
if ((0 != _reply.readInt())) {
_result = remote.Student.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static remote.IStudentService sDefaultImpl;
}
static final int TRANSACTION_getStudentById = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
public static boolean setDefaultImpl(remote.IStudentService impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static remote.IStudentService getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
public remote.Student getStudentById(int id) throws android.os.RemoteException;
}自定义服务进行跨进程通信的过程:(1)创建一个AIDL文件,内部定义了服务端进程想要提供给客户端进程的方法列表,然后系统会生成对应的一个同名Java文件;(2)自定义服务通过Service实现,在里面实现提供给客户端的方法的具体逻辑,之后客户端bindService后,在Service的onBind方法返回这个服务端对应的Binder对象,然后客户端就能在ServiceConnection中onServiceConnected这个回调方法当中获得服务端对应的Binder对象;(3)客户端获得了Binder对象之后会调用例如IStudentService.Stub.asInterface(IBinder service)将Binder对象传入,获取remote.IStudentService对象,从这里开始就跟跨进程通信分开了,如果是跨进程通信asInterface返回的是IStudentService.Stub.Proxy代理对象,以后客户端调用服务端的方法实际上就是调用IStudentService.Stub.Proxy代理对象里面对应于服务端的方法,会通过transact陷入内核态来进行实际上的进程间通信去调用服务端的onTransact方法,在onTransact方法中会根据传入标志调用不同的服务端方法; 而如果是同进程通信的话,asInterface返回的是IStudentService.Stub对象,然后直接调用服务端的方法,而不必陷入内核态执行了,可以是在Service类当中继承IStudentService.Stub抽象类实现抽象方法然后在onBind()方法返回这个实现类的实例。
在使用AIDL实现Binder通信的过程中,无论服务端方法还是客户端方法都是运行在各自的Binder线程池中的,如果我们想要更新UI的话,就需要用到Handler进行切换操作。
AIDL
Binder通信机制的具体应用。
常用系统服务
传入的Name | 返回的对象 | 说明 |
|---|---|---|
WINDOW_SERVICE | WindowManager | 管理打开的窗口程序 |
ACTIVITY_SERVICE | ActivityManager | 管理应用程序的系统状态 |
NOTIFICATION_SERVICE | NotificationManager | 状态栏服务 |
LAYOUT_INFLATER_SERVICE | LayoutManager | 取得XML里面定义的View |
ALARM_SERVICE | AlarmManager | 闹钟服务 |
POWER_SERVICE | PowerManager | 电源服务 |
获取示例:
WindowManager windowManager= Context.getSystemService(Context.WINDOW_SERVICE);
参考
BInder系列开篇
Binder原理剖析未看完
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/155022.html原文链接:https://javaforall.cn