关于Binder机制的学习和理解

1、Android为什么选用Binder?
虽然Linux已经存在很多IPC的方式,比如Socket、管道、消息队列、共享内存等,但是这些方式还是存在一些不足的地方。
a、首先传统的IPC机制没有安全措施,接收方无法获取对方进程的进程ID或者UID,完全需要依靠上层的协议,比如用户密码这些来保护。但是Android系统为每一个应用程序都分配了用户ID(UID),通过UID可以甄别进程的身份,控制权限。
b、其次是性能方面考虑,管道、消息队列、Socket实现一次跨进程通讯需要2次内存拷贝,而共享内存管理起来又很复杂;但是Binder实现一次通讯却只需要拷贝一次内存,性能大大提高。

2、Binder的底层原理
由于进程隔离,两个进程间的用户空间不能直接通信,而需要经过内核空间的支持,Binder也一样。传统的Linux IPC方式,是先把进程用户空间的数据拷贝到内核缓存区,然后再从内核缓存区拷贝到接收方的内存缓存区中,这就需要两次拷贝。而Binder方式的IPC,首先也是需要把发送方的数据从用户空间拷贝到内核空间,但是后面,Binder驱动程序在内核空间的虚拟内存地址和接收方的虚拟内存地之间做了一个映射,这样接收方就可以直接去读取这个地址上的数据了,减少了一次拷贝。

3、Binder的通信模型
在Binder机制中,定义了4个角色:Client、Server、Binder驱动和ServiceManager。具体的通信过程如下:
a、Server通过Binder驱动向ServiceManager注册,声明可以对外提供服务。ServiceManager中会保留一份映射表:名字为demo的Server对应的Binder引用是0x12345。
b、Client想要请求Server的数据时,需要先通过Binder驱动向ServiceManager查找到Server的Binder引用:我想要名字为demo的Server的Binder应用。
c、Client拿到这个Binder引用之后,就可以通过Binder驱动和Server通信了。
d、Server收到请求后,通过Binder驱动返回结果。

4、Binder的代理机制
Client向ServiceManager获取到的Binder引用,其实是一个Proxy代理对象,它不是真正的Server里面的Binder,只是有着和实际Binder相同的方法声明。当用户端调用了Proxy代理对象里面的方法之后,底层会通过Binder驱动的转换而去调用实际Binder中相同的方法。这就是Binder的代理机制。

5、AIDL方式使用Binder
在Android SDK中提供了AIDL接口,让开发者可以很方便地使用Binder通信。下面是几个与Binder相关几个的类的职责:
IBinder: 跨进程通信的Base接口,它定义了跨进程所需要的一系列抽象接口,实现了这个接口就说明具有跨进程的能力,Client和Server都需要实现这个接口。
IInterface:这也是一个base接口,应用程序中的业务接口需要继承这个接口,从而能够被方便地管理。
Binder:提Binder服务的本地对象的基类,它实现了IBinder接口,是本地服务实际具有跨进程通信的类,所有本地服务对象都需要实现这个类。
BinderProxy:这个类表示Binder代理对象,它同样实现了IBinder接口,不过它的很多实现都交由native层处理。Client端拿到的其实就是这个代理对象。
Stub:这个类在编译aidl文件的时候自动生成,它继承Binder,代表它是Server本地对象,它是一个抽象类,需要在Server端实现这个类,以及它里面具体的业务方法。
Proxy:它实现了IInterface接口,说明是Binder通讯的一部分。它实现了aidl中声明的方法,但最终是由其成员mRemote来完成具体的功能,说明它是一个代理对象,这个mRemote成员其实就是BinderProxy。

aidl文件:

package com.example.aidl;
interface TestServer{
    int[] getdatas();
}

生成的java文件:

package com.example.aidl;
public interface TestServer extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.aidl.TestServer
{
private static final java.lang.String DESCRIPTOR = "com.example.aidl.TestServer";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.example.aidl.TestServer interface,
 * generating a proxy if needed.
 */
public static com.example.aidl.TestServer asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.aidl.TestServer))) {
return ((com.example.aidl.TestServer)iin);
}
return new com.example.aidl.TestServer.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@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_getdatas:
{
data.enforceInterface(DESCRIPTOR);
int[] _result = this.getdatas();
reply.writeNoException();
reply.writeIntArray(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.aidl.TestServer
{
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 int[] getdatas() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int[] _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getdatas, _data, _reply, 0);
_reply.readException();
_result = _reply.createIntArray();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getdatas = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public int[] getdatas() throws android.os.RemoteException;
}

这个文件里面有TestServer 、TestServer .Stub、TestServer .Stub.Proxy这个几个类,之所以都写在同一个java文件,是为了方便用户调用和管理,但是需要清除地知道它们各自的职责所在。

在服务端,我们需要去实现TestServer .Stub这个抽象类,实现getdata()这个方法,同时在服务onBind()方法的时候,返回这个实现类的实例。需要说明的是,实例化这个本地Binder类的时候,使用的是父类Binder的构造方法

public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}

这个时候Binder会把对象和描述字符串DESCRIPTOR保存起来,后面会用到。

TestServer .Stub.asInterface(android.os.IBinder obj)这个方法,通常是在Client端使用的,它是把一个实际的IBinder转换成我们能够使用的接口,以便调用定义的业务方法。由于Binder机制中可以让Client和Server在同一个进程中,因此,如果双方都在同一个进程,那么返回的类型就是Binder本地的对象,也就是TestServer .Stub的子类;如果不在同一个进程中,那么返回的则是一个Binder的代理对象,即TestServer .Stub.Proxy对象。
如何判断是否在同一线程呢,这就用到了我们上面说的创建本地Binder对象时候保存的字符串DESCRIPTOR了,如下:

public static com.example.aidl.TestServer asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.aidl.TestServer))) {
return ((com.example.aidl.TestServer)iin);
}
return new com.example.aidl.TestServer.Stub.Proxy(obj);
}
//Binder.java
/**
 * obj.queryLocalInterface是怎样去查找是否有本地的IInterface呢,从Binder的代码中可以看到,只是简单的比较Binder的描述符和要查找的描述符是否匹配,匹配的话直接返回mOwner,这个mOwner就是Stub构造方法中调用attachInterface方法传入的this参数。
 */
public IInterface queryLocalInterface(String descriptor) {
    if (mDescriptor.equals(descriptor)) {
        return mOwner;
    }
    return null;
}

参考博客:https://www.jianshu.com/p/062a6e4f5cbe

猜你喜欢

转载自blog.csdn.net/yus201120/article/details/81667767
今日推荐