1: 概述
Binder这个东西, 之前看过, 迷糊的狠, 只是了解了基本的原理, 具体到代码层就比较模糊了。
关键是脑袋里没有一个基本的Binder的通信模型。
所以先从上层开始分析使用, 然后分析模型, 最后再到代码。 任何东西如果脑袋里有了一个基本的模型后, 任何事情的分析都会事半功倍。 细节的分析, 只是丰富这个模型罢了。
废话结束。
2: Java层的AIDL
如果不了解什么是AIDL就没法说java层的Binder。
AIDL超级简单就是辅助用户编写Java端的Binder通信的代码使用。
例如你想写一个基于Binder的跨进程远程通信机制:
1) 准备工作
先写一个简单的AIDL接口文件
(IGank.aidl), 然后用工具生成JAVA文件
(IGank.java 这个接口文件包含三部分: 【1】你在AIDL文件里面定义的接口(IGank) 【2】 一个Stub类(IGank.stub), 用于服务端 【3】 一个Proxy类(IGank.stub.Proxy), 用于客户端), 准备工作结束。
2) 服务端
服务端继承 Stub 类, 并实现接口, 就这么简单。
3) 客户端
客户端更加简单, 只要使用
IGank.Stub.asInterface方法, 把一个Binder对象转换成IGank对象就可以了。 用户可以直接使用IGank的方法, 这里通过Binder, 服务端相应的方法也会被调用。
3: 以一个JAVA层系统的Binder服务进行分析
这里选的是InputManagerService。 先上图有一个大致了解。
1) 服务端分析
服务端按照上面分析AIDL的方式分析, 就特别简单。
首先InputManagerService 需要定义了一系列的方法用于客户端的调用 (定义方法的文件是 IInputManager.aidl文件)
接着InputManagerService 就需要实现了, 这个时候只需要继承 IInputManager.Stub 类就可以了 (这个文件上面已经介绍是通过工具自动生成的)
2) 客户端分析
客户端的分析, 我们以一次 InputManagerService 中方法调用的过程进行介绍。
1: 如何获取InputManager
通常使用 Context 的 public Object getSystemService(String name) 方法。
然后强制转换成响应的SystemService.
这个方法实现在ContextImpl.java
public Object getSystemService(String name) {
ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
return fetcher == null ? null : fetcher.getService(this);
}
registerService(INPUT_SERVICE, new StaticServiceFetcher() {
public Object createStaticService() {
return <span style="color:#ff0000;">InputManager.getInstance();</span>
}});
可以看到所有系统服务都注册到到一个以服务名为Key的Map上(SYSTEM_SERVICE_NAME).
由于InputManager是就是一个Singleton模式, 所以实际返回的InputManager就是一个静态对象。
2: InputManager初始化流程
public static InputManager <span style="color:#ff0000;">getInstance</span>() {
synchronized (InputManager.class) {
if (sInstance == null) {
IBinder b = ServiceManager.<span style="color:#ff0000;">getService</span>(Context.INPUT_SERVICE);
sInstance = new InputManager(IInputManager.Stub.<span style="color:#ff0000;">asInterface</span>(b));
}
return sInstance;
}
}
第一步
是获取服务端注册的Binder对象。 这部分主要是到ServerManager中去查找相应的Service的Binder。
Slog.i(TAG, "Input Manager");
inputManager = new InputManagerService(context, wmHandler);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
在SystemServer.java中开启相应的服务的时候, 把相应的服务注册到系统的systemserver服务中, 以供上面 ServiceManager.getService(Context.INPUT_SERVICE); 通过服务名获取相应的Binder对象。
public static android.hardware.input.IInputManager asInterface(
android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof android.hardware.input.IInputManager))) {
return ((android.hardware.input.IInputManager) iin);
}
return new android.hardware.input.IInputManager.Stub.Proxy(obj);
}
通过这部分的代码不难看出, 转换的最终调用new android.hardware.input.IInputManager.Stub.Proxy(obj);
这里就看到了InputManager端实际是InputManagerService的一个代理端(PS: Proxy 实现IInputManager接口, 所以可以被强制转换成IInputManager对象)。
private final IInputManager mIm;
private InputManager(IInputManager im) {
mIm = im;
}
这里看到InputManager会保存一个
InputManagerService的代理对象。
看图上的虚线, 可以看出来InputManager实际上保存一个InputManagerService的一个代理对象。
3: 服务端InputMangerService如何响应
这个就是Proxy如何调用Stub的问题了。
假设服务端调用InputManager.deviceHasKeys方法
public boolean[] deviceHasKeys(int id, int[] keyCodes) {
boolean[] ret = new boolean[keyCodes.length];
try {
mIm.hasKeys(id, InputDevice.SOURCE_ANY, keyCodes, ret);
} catch (RemoteException e) {
// no fallback; just return the empty array
}
return ret;
}
实际调用的是Proxy的hasKeys方法。
@Override
public boolean hasKeys(int deviceId, int sourceMask, int[] keyCodes,
boolean[] keyExists) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
boolean _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(deviceId);
_data.writeInt(sourceMask);
_data.writeIntArray(keyCodes);
if ((keyExists == null)) {
_data.writeInt(-1);
} else {
_data.writeInt(keyExists.length);
}
<span style="color:#ff0000;">mRemote.transact(Stub.TRANSACTION_hasKeys, _data, _reply, 0);</span>
_reply.readException();
_result = (0 != _reply.readInt());
_reply.readBooleanArray(keyExists);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
这个函数实际调用了远程的transact方法, 也就是Stub.transact方法。
由于Stub继承了Binder, transact实际是Binder中的方法, 这个方法只是简单的调用相应的onTransact方法。
public final boolean transact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {
if (false) Log.v("Binder", "Transact: " + code + " to " + this);
if (data != null) {
data.setDataPosition(0);
}
boolean r = <span style="color:#ff0000;">onTransact(code, data, reply, flags);</span>
if (reply != null) {
reply.setDataPosition(0);
}
return r;
}
@Override
public boolean onTransact(int code, android.os.Parcel data,
android.os.Parcel reply, int flags)
throws android.os.RemoteException {
switch (code) {
...
case TRANSACTION_hasKeys: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int[] _arg2;
_arg2 = data.createIntArray();
boolean[] _arg3;
int _arg3_length = data.readInt();
if ((_arg3_length < 0)) {
_arg3 = null;
} else {
_arg3 = new boolean[_arg3_length];
}
boolean _result = <span style="color:#ff0000;">this.hasKeys(_arg0, _arg1, _arg2, _arg3);</span>
reply.writeNoException();
reply.writeInt(((_result) ? (1) : (0)));
reply.writeBooleanArray(_arg3);
return true;
}
...
}
}
可以看到这里根据Code解析相应的参数, 然后再调用InputManagerService中实际的方法, 至此整个调用逻辑结束, 最后用一张图总结一下。
总结
Java层的Binder, 即使对于App的开发者来说也是透明的。
其作用很简单就是跨进程间的通讯。
实现方式, 使用了代理模式来实现。
服务端
实现Stub, 客户端
通过Proxy对象调用服务端, 中间的通信逻辑是急于Binder的通信, 并且封装到用户不知道是用Binder来通信的。