为什么学习Binder机制
- Binder是Android系统中最重要的特性之一;正如其名“粘合剂”所喻,它是系统间各个组件的桥梁,Android系统的开放式设计也很大程度上得益于这种及其方便的跨进程通信机制。
- Binder是一种进程间通信机制,能帮助我们进行不同进程间的信息交流,以及数据通信。
- Binder相对出传统的Socket方式,更加高效;
- 传统的进程通信方式对于通信双方的身份并没有做出严格的验证,只有在上层协议上进行架设;比如Socket通信ip地址是客户端手动填入的,都可以进行伪造;而Binder机制从协议本身就支持对通信双方做身份校检,因而大大提升了安全性。这个也是Android权限模型的基础。
binder通信模型
- SM建立(建立通信录);首先有一个进程向驱动提出申请为SM;驱动同意之后,SM进程负责管理Service(注意这里是Service而不是Server,因为如果通信过程反过来的话,那么原来的客户端Client也会成为服务端Server)不过这时候通信录还是空的,一个号码都没有。
- 各个Server向SM注册(完善通信录);每个Server端进程启动之后,向SM报告,我是zhangsan, 要找我请返回0x1234(这个地址没有实际意义,类比);其他Server进程依次如此;这样SM就建立了一张表,对应着各个Server的名字和地址;就好比B与A见面了,说存个我的号码吧,以后找我拨打10086;
- Client想要与Server通信,首先询问SM;请告诉我如何联系zhangsan,SM收到后给他一个号码0x1234;Client收到之后,开心滴用这个号码拨通了Server的电话,于是就开始通信了。
binder驱动的作用
在Android系统中,这个运行在内核空间的,负责各个用户进程通过Binder通信的内核模块叫做Binder驱动;
* 简单来说:就是Client请求服务对象,Binder驱动查询ServiceManager(SM)里是否有该服务,若有返回一个代理对象,可以理解为远程Service对象的映射(影子),这个映射对象拥有和远程Service对象一样的方法,但是这个映射对象和他的这些方法只是作为中介的作用,对Client的参数经过包装后交给Binder驱动,Binder驱动通知该service对象调用真正的方法对数据进行处理,将结果返回给Binder驱动,驱动再把结果返回给Client端
* Client进程的操作其实是对于影子的操作,影子利用Binder驱动最终让真身完成操作。
* 给人感觉好像是直接把Server进程里面的对象object传递到了Client进程;因此,我们可以说Binder对象是可以进行跨进程传递的对象
* Android系统实现这种机制使用的是代理模式, 对于Binder的访问,如果是在同一个进程(不需要跨进程),那么直接返回原始的Binder实体;如果在不同进程,那么就给他一个代理对象(影子);我们在系统源码以及AIDL的生成代码里面可以看到很多这种实现。
* Client进程只不过是持有了Server端的代理;代理对象协助驱动完成了跨进程通信。
什么是Binder
- 通常意义下,Binder指的是一种通信机制;我们说AIDL使用Binder进行通信,指的就是Binder这种IPC机制。
- 对于Server进程来说,Binder指的是Binder本地对象
- 对于Client来说,Binder指的是Binder代理对象,它只是Binder本地对象的一个远程代理;
- 对这个Binder代理对象的操作,会通过驱动最终转发到Binder本地对象上去完成;对于一个拥有Binder对象的使用者而言,它无须关心这是一个Binder代理对象还是Binder本地对象;对于代理对象的操作和对本地对象的操作对它来说没有区别。
- 对于传输过程而言,Binder是可以进行跨进程传递的对象;Binder驱动会对具有跨进程传递能力的对象(使用Parcel?)做特殊处理:自动完成代理对象和本地对象的转换。
深入理解Java层的Binder
- IBinder是一个接口,它代表了一种跨进程传输的能力;只要实现了这个接口,就能将这个对象进行跨进程传递;这是驱动底层支持的;在跨进程数据流经驱动的时候,驱动会识别IBinder类型的数据,从而自动完成不同进程Binder本地对象以及Binder代理对象的转换。
- IBinder负责数据传输,那么client与server端的调用契约(这里不用接口避免混淆)呢?这里的IInterface代表的就是远程server对象具有什么能力。具体来说,就是aidl里面的接口。
- Java层的Binder类,代表的其实就是Binder本地对象。BinderProxy类是Binder类的一个内部类,它代表远程进程的Binder对象的本地代理;这两个类都继承自IBinder, 因而都具有跨进程传输的能力;实际上,在跨越进程的时候,Binder驱动会自动完成这两个对象的转换。
- 在使用AIDL的时候,编译工具会给我们生成一个Stub的静态内部类;这个类继承了Binder, 说明它是一个Binder本地对象,它实现了IInterface接口,表明它具有远程Server承诺给Client的能力;Stub是一个抽象类,具体的IInterface的相关实现需要我们手动完成,这里使用了策略模式。
手动撸远程服务代码遇到的问题(Framework层Binder的使用)
- add Service是出现java.lang.reflect.InvocationTargetException;因为ServiceManager类是被隐藏的,这里我用反射的方法执行addService方法(Framework层Binder的使用)
try {
//注册服务
serviceManager = Class.forName("android.os.ServiceManager");
// serviceManager.getDeclaredField()
Method addService = serviceManager.getDeclaredMethod("addService", String.class, IBinder.class);
addService.setAccessible(true);
addService.invoke(serviceManager,"MyService",new MyService());
} catch (Exception e) {
e.printStackTrace();
}
因为在addService的时候出现这个异常,所以在我getService的时候返回的是空,所以就一直不成功,有大神看到个话,能不能高抬贵手��(-0-),给个解决方法,因为我这是求助贴啊!!!
Framework层Binder的理解
虽然我写的Demo不成功,但不妨碍有大神写的例子啊,看了大神写的文章好几遍,说一下我对Framework层Binder的使用的浅薄的理解,不喜勿喷,敬请指正(��-0-��),谢谢!
客户端
1.获取特定的服务
//获取名为"MyService"的服务
IBinder binder = ServiceManager.getService("MyService");
2.设置同一接口并实现其方法(你需要其有什么样的功能(能力?))
public interface IMyService extends IInterface {
static final java.lang.String DESCRIPTOR = "com.example.binderdemo.service.MyServer";
void upGrade(String weapon) throws RemoteException;
static final int TRANSACTION_say = android.os.IBinder.FIRST_CALL_TRANSACTION;
}
public class MyServiceProxy implements IMyService {
private final IBinder mIbinder;
public MyServiceProxy(IBinder iBinder){
this.mIbinder=iBinder;
}
public String getInterfaceDescriptor(){
return DESCRIPTOR;
}
@Override
public void upGrade(String weapon) throws RemoteException {
Parcel obtain_data = Parcel.obtain();
Parcel obtain_reply = Parcel.obtain();
try{
//这是用来验证该编组交易旨在用于目标接口。
obtain_data.writeInterfaceToken(DESCRIPTOR);
//一个字符串值转换成Parcel
obtain_data.writeString(weapon);
//默认实现倒带Parcel并调用onTransact。在远端,transact调用绑定器来执行IPC。
mIbinder.transact(TRANSACTION_say, obtain_data, obtain_reply, 0);
obtain_reply.readException();
}finally {
obtain_reply.recycle();
obtain_data.recycle();
}
}
@Override
public IBinder asBinder() {
return mIbinder;
}
}
在这里最重要的便是Parcel类了,Binder对象是Android的通用跨进程通信系统的核心设施。该IBinder接口描述了具有Binder对象的抽象协议。任何这样的接口可以写入到一个Parcel,并在接受是您会收到一个实现该接口或传达回调到原来的对象特殊的代理实现原来的目标。
这是官方文档上的一句话,按我的理解就是把数据用Parcel进行序列化包裹传输,在Service端在读出来进行处理!
- 远程Service端
1.注册服务
//注册服务
ServiceManager.addService("MyService", new MyService());
2.Service端接口,并实现
public interface IMyService extends IInterface {
static final java.lang.String DESCRIPTOR = "com.example.binderdemo.service.MyServer";
void upGrade(String weapon) throws RemoteException;
static final int TRANSACTION_say = android.os.IBinder.FIRST_CALL_TRANSACTION;
}
public class MyService extends Binder implements IMyService {
private static final String TAG = "MyService.class";
public MyService(){
this.attachInterface(this,DESCRIPTOR);
}
/** 将MyService转换为IMyService接口 **/
public static IMyService asInterface(IBinder iBinder){
if(iBinder==null){
return null;
}
IInterface iInterface = iBinder.queryLocalInterface(DESCRIPTOR);
if(iInterface!=null&&iInterface instanceof IMyService){
return (IMyService) iInterface;
}
return null;
}
/**
服务端,接收远程消息,处理onTransact方法
默认实现是返回false的存根。您将希望重写此操作,以执行适当的事务处理。
如果您想调用此函数,请调用transact()。
**/
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code){
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_say:{
//执行接口
data.enforceInterface(DESCRIPTOR);
String readString = data.readString();
upGrade(readString);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
@Override
public void upGrade(String weapon) throws RemoteException {
Log.e(TAG, "upGrade:Take the"+ weapon+",Go to the Strong");
Log.e(TAG, "我拿着"+ weapon+",砍了个神兽,升级了");
System.out.println("upGrade:Take the weapon,Go to the Strong");
}
@Override
public IBinder asBinder() {
return this;
}
}
最后推荐两位大神写个文章,让我学到了很多!!!
- Binder学习指南
-
拼搏在技术道路上的一只小白And成长之路