关于Binder机制,看了2天还是“糊里糊涂”的,该总结自己理解的Binder了。纯属自己总结,不对的地方,请多多指正。
Binder机制有啥用
Binder机制主要解决的是IPC(Inner Process Communicate),进程间通信问题。不同进程管理者自己的“东西”(内存),有属性,方法之类的,如果进程A想访问进程B的“东西”,只能通过IPC实现。
IPC的形式有很多种,如共享内存,管道,socket,文件等等,至于Android为什么还要自己设计一套专门的机制去实现IPC,主要考虑2方面:(1)性能(只拷贝一次)。(2)安全。
Binder机制的4个角色
(1)Client: 客户端,可以是任意一个进程,是远程(服务)请求的发起者。
(2)Server: 服务端,可以是任意一个进程,主要提供服务,请求的执行者。
(3)ServiceManager: Server需要在这里注册自己,提供自己的信息,Client可以在这里获取Server的引用。
(4)Binder驱动: Server和Client通信的媒介。
Binder机制实现IPC的流程
新建Server后,需要在ServiceManager注册,提供自己的信息。ServiceManager会根据Server的信息,生成IBinder引用,相当于Server的“引用“。
Client向ServiceManager查询目标Server,并获取目标Server的IBinder引用。有没有发现,Client向ServiceManager查询Server,也是一个IPC的过程,此时ServiceManager也是一个Server。由于ServiceManger的IBinder是固定的,而且别所有人知道的,所以Client还是很容易获取ServiceManager的IBinder,从而访问ServiceManager。ServiceManager会返回目标Server的IBinder对象,此时Client就可以与Server“通信“了。
Client发出请求,调用Server的方法,此时Client的线程会挂起,Binder驱动会把Client要传送的数据从Client的用户空间复制到Server内核空间的内存中,因为Server内核空间的内存已经通过mmap映射到Server的用户空间中,Binder驱动只需要将刚才复制之后的数据所在的内核空间的地址告诉Server就行,这样Server就可以获取Client传送过来的数据了。这就是传说中,一次拷贝。相反,待Server执行完请求的方法后,Binder驱动通过相同的方法,将Server的结果数据从Server的用户空间复制到Client的内核空间中,并将地址告诉Client即可。因为Client请求时,线程被挂起,现在结果已经有了,Binder驱动唤起Client线程,通知Client请求的方法已经执行完了,你可以继续干你的活了。
Binder机制的使用:AIDL(Android Interface Define Language)
我们从使用的代码进一步了解Binder机制。两个不同进程通信,需要统一“语言”,需要做一定的规范,要不然进程A怎么知道进程B有什么呢。所以就有了AIDL。
我们先创建一个有计算功能的Server,提供Client计算用。
创建一个Calculator.aidl,代码如下:
package com.johan.aidl;
interface Calculator {
int add(int num1, int num2);
int sub(int num1, int num2);
}
eclipse工具就会调用aidl.exe自动帮我们生成对应的名为Calculator的java文件(在gen目录下),代码如下:
package com.johan.aidl;
public interface Calculator extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.johan.aidl.Calculator {
private static final java.lang.String DESCRIPTOR = "com.johan.aidl.Calculator";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.johan.aidl.Calculator interface,
* generating a proxy if needed.
*/
public static com.johan.aidl.Calculator asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.johan.aidl.Calculator))) {
return ((com.johan.aidl.Calculator) iin);
}
return new com.johan.aidl.Calculator.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_add: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_sub: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.sub(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.johan.aidl.Calculator {
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 add(int num1, int num2) 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);
_data.writeInt(num1);
_data.writeInt(num2);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public int sub(int num1, int num2) 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);
_data.writeInt(num1);
_data.writeInt(num2);
mRemote.transact(Stub.TRANSACTION_sub, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_sub = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public int add(int num1, int num2) throws android.os.RemoteException;
public int sub(int num1, int num2) throws android.os.RemoteException;
}
我们开始解析eclipse帮我们生成的类。首先是这个类是一个继承IInterface 接口,don’t care。这个接口定义了两个内部类,Stub和Proxy。
我们先看看Stub(存根)这个类,这个类是一个抽象类,继承Binder,证明他就是一个Binder,我们写一个服务类时,继承Service,当客户端bindService时,返回的就是这个Binder的子类。还实现了我们定义的aidl接口,交给子类去实现。我们看他的构造函数:
private static final java.lang.String DESCRIPTOR = "com.johan.aidl.Calculator";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
构造函数里的attachInterface的作用是保存自己的描述和引用,下面会有用到,继续看看asInterface这个方法:
public static com.johan.aidl.Calculator asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.johan.aidl.Calculator))) {
return ((com.johan.aidl.Calculator) iin);
}
return new com.johan.aidl.Calculator.Stub.Proxy(obj);
}
obj.queryLocalInterface(DESCRIPTOR)根据描述查看本地是否有目标服务类,如果想调用的服务正好和自己在同一个进程,直接返回找到的服务类,如果没有找到,说明目标服务类和自己不在同一个进程,则返回Proxy代理类。这下我们得好好看看这个代理类,单独拿出来:
private static class Proxy implements com.johan.aidl.Calculator {
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 add(int num1, int num2) 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);
_data.writeInt(num1);
_data.writeInt(num2);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public int sub(int num1, int num2) 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);
_data.writeInt(num1);
_data.writeInt(num2);
mRemote.transact(Stub.TRANSACTION_sub, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
代理类持有服务类的IBinder引用,也实现了我们定义的aidl接口,所以他“算是”有服务类功能。一般客户端都会获取到的服务类,都是这个代理类。我们调用这个代理服务类的方法,如add方法,代码:
@Override
public int add(int num1, int num2) 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);
_data.writeInt(num1);
_data.writeInt(num2);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
会先从池中取出两个Parcel对象存放传送的数据和返回的结果,然后调用IBinder的transact方法,此时,客户线程会挂起。通过Binder驱动,服务进程的Stub(真正的服务类)会调用onTransact方法。IBinder调用transact方法时,传入Stub.TRANSACTION_add值,这就指明要服务类调用add方法,参数通过_data传进去,服务类执行完目标方法后,会把结果写入_reply,此时,Binder驱动会唤醒客户线程,代理服务类通过读取_reply数据返回给客户。
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_sub = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
AIDL为我们接口每一个方法定义了一个标志,好让服务类知道客户请求的是哪个方法。AIDL算是分析完了,如果可以的话,建议结合例子会更加深入。

本文详细解析了Android中的Binder机制,介绍了其在进程间通信中的作用、实现原理及使用方法。通过AIDL示例深入理解Binder的工作流程。
699

被折叠的 条评论
为什么被折叠?



