Binder Java层的使用介绍

来源:互联网 发布:c语言中‖是什么意思 编辑:程序博客网 时间:2024/05/29 07:37

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方法。
@Overridepublic 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;    }

@Overridepublic 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来通信的。

0 0
原创粉丝点击