Binder Java层的使用介绍

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值