Android Binder Android Binder的原理与使用
板砖不得颈椎病 人气:0前言
Binder是安卓中实现IPC(进程间通信的)常用手段,四大组件之间的跨进程通信也是利用Binder实现的,Binder是学习四大组件工作原理的的一个重要基础。 好多文章都会深入C代码去介绍Binder的工作流程,没点水平真的难以理解,本文不会太深入底层去剖析原理,尽可能较为简单的让大家了解Binder是怎么工作的。
Binder的使用
在介绍Binder原理之前,我们先来看看在安卓中怎么使用Binder来进程间通信。 在使用之前我们先来介绍Binder的几个方法:
public final boolean transact(int code, Parcel data, Parcel reply, int flags)
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
这两个方法分别代表了客户端和服务端,transact用来发送消息,onTransact负责接收transact传过来的消息,这一点很容易理解。
- code 方法标识符,在相同进程中,我们很容易的通过方法调用来执行我们的目标方法,但是在不同的进程间,方法调用的方式就不能再用了,所以我们使用code来表示远程调用函数的标识。这个标识必须介于FIRST_CALL_TRANSACTION(0x00000001)和LAST_CALL_TRANSACTION(0x00ffffff)之间。
- data Parcel类型的数据包,要传给客户端的请求参数。
- reply 如果客户端需要返回值,则reply就是服务端返回的数据。
- flags 用来区分这个调用是普通调用还是单程调用,普通调用时,Client端线程会阻塞,直到从Server端接收到返回值,若flag==IBinder.FLAG_ONEWAY,则这次调用是单程调用,Client在传出数据后会立即执行下一段代码,此时两端异步执行,单程调用时函数返回值必须为void (也就是单程调用必须舍弃返回值,要返回值就必须阻塞等待)
利用这两个方法我们就可以实现Client和Server端的通信,接下来我们看看具体该怎么使用。 在Server接收到Client传来的消息(data)时,会对data进行验证data.enforceInterface(DESCRIPTOR),DESCRIPTOR是一个字符串类型的描述符,当data的描述符跟DESCRIPTOR相同时才能通过验证。
public class Stub extends Binder { //描述符 public static final java.lang.String DESCRIPTOR = "MyTestBinder"; //code 方法描述符 public static final int TRANSACTION_test0 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); public static final int TRANSACTION_test1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { switch (code) { case TRANSACTION_test0: //验证描述符 data.enforceInterface(DESCRIPTOR); //执行方法 test0(); return true; case TRANSACTION_test1: //验证描述符 data.enforceInterface(DESCRIPTOR); //执行方法 test1(data, reply); return true; } return super.onTransact(code, data, reply, flags); } //无返回值 private void test0() { Log.d("MyBinderServer", "test0"); } //有返回值 private void test1(Parcel data, Parcel reply) { Log.d("MyBinderServer", "test1"); reply.writeInt(data.readInt() + 1); } }
我们知道,要想实现Client和Server端的通信连接,就必须让client知道server端的地址,就跟Http请求,我们要知道服务端的ip和端口。Binder通信其实也是一样的,那么我们怎么让Client拿到Server的地址呢? 一种是跟Http请求一样,我们知道Http请求要把域名转换成ip和端口,这就是DNS,我们也需要一个Binder的DNS。安卓中也为我们提供了Binder的“DNS”那就是ServiceManager,ServiceManager中注册了所有系统服务(如MediaServer等),我们可以使用ServiceManager拿到远程的Binder地址,这种方式叫做有名Binder查找(有名Binder,如MediaServer等这些系统服务被注册的时候都是有名字的,比如,我们通过WINDOW_SERVICE这个名字就能拿到WindowManager)。但是问题是向ServiceManager注册服务的过程是系统进程实现的,我们的应用进程不能注册自己的Binder。 另一种就是利用有名的Binder来辅助传递匿名的Binder,也就是说如果有某个有名Binder服务它提供了传递Binder的方法,那么我们就可以通过这个Binder服务来传递我们的匿名Binder,我们查找到这个有名的Binder是不是就能拿到我们的匿名Binder。正好AMS其实提供了这样的功能,它通过Service.onBind把匿名的Binder封装在了Service里面供我们调用。
public class MyService extends Service { @Override public IBinder onBind(Intent intent) { return new Stub(); } }
我们使用binderService()来获取远程的Binder。
Intent serviceIntent = new Intent(this, MyService.class); bindService(serviceIntent, new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { //service可以理解为是远程Binder的地址,我们利用他跟远程通信,C++层会转换这个IBinder跟Binder进行通信 } @Override public void onServiceDisconnected(ComponentName name) { } }, BIND_AUTO_CREATE);
获取到Binder之后我们补充好通信的代码:
bindService(serviceIntent, new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Parcel data0 = Parcel.obtain();//请求参数 Parcel reply0 = Parcel.obtain();//响应参数 Parcel data1 = Parcel.obtain(); Parcel reply1 = Parcel.obtain(); //调用第一个方法 try { //添加描述符 data0.writeInterfaceToken(Stub.DESCRIPTOR); /* * 写入参数,要想传递多个int参数,顺序调用writeInt * data0.writeInt(10); * data0.writeInt(20); * 获取 * int num10 = data0.readInt(); * int num20 = data0.readInt(); */ data0.writeInt(10); //发起远程调用 service.transact(Stub.TRANSACTION_test0, data0, reply0, 0); reply0.readException(); } catch (RemoteException e) { e.printStackTrace(); } finally { data0.recycle(); reply0.recycle(); } //调用第二个方法 try { //添加描述符 data1.writeInterfaceToken(Stub.DESCRIPTOR); data1.writeInt(10); //发起远程调用 service.transact(Stub.TRANSACTION_test1, data1, reply1, 0); reply1.readException(); //读取返回值 int num = reply1.readInt(); Log.d(TAG, "reply value: " + num); } catch (RemoteException e) { e.printStackTrace(); } finally { data1.recycle(); reply1.recycle(); } } @Override public void onServiceDisconnected(ComponentName name) { } }, BIND_AUTO_CREATE);
为了方便调用,我们写一个代理类来封装通信过程
public class Proxy { //描述符 public static final java.lang.String DESCRIPTOR = "MyTestBinder"; //code 方法描述符 public static final int TRANSACTION_test0 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); public static final int TRANSACTION_test1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); private IBinder mRemote; public Proxy(IBinder remote) { this.mRemote = remote; } public void test1() { Parcel data0 = Parcel.obtain();//请求参数 Parcel reply0 = Parcel.obtain();//响应参数 //调用第一个方法 try { //添加描述符 data0.writeInterfaceToken(DESCRIPTOR); /* * 写入参数,要想传递多个int参数,顺序调用writeInt * data0.writeInt(10); * data0.writeInt(20); * 获取 * int num10 = data0.readInt(); * int num20 = data0.readInt(); */ data0.writeInt(10); //发起远程调用 mRemote.transact(TRANSACTION_test0, data0, reply0, 0); reply0.readException(); } catch (RemoteException e) { e.printStackTrace(); } finally { data0.recycle(); reply0.recycle(); } } public int test2() { Parcel data1 = Parcel.obtain(); Parcel reply1 = Parcel.obtain(); //调用第二个方法 int num = 0; try { //添加描述符 data1.writeInterfaceToken(DESCRIPTOR); data1.writeInt(10); //发起远程调用 mRemote.transact(TRANSACTION_test1, data1, reply1, 0); reply1.readException(); //读取返回值 num = reply1.readInt(); } catch (RemoteException e) { e.printStackTrace(); } finally { data1.recycle(); reply1.recycle(); } return num; } }
bindService(serviceIntent, new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Proxy proxy = new Proxy(service); proxy.test1(); int i = proxy.test2(); } @Override public void onServiceDisconnected(ComponentName name) { } }, BIND_AUTO_CREATE);
模糊进程间调用
前边就是Binder的使用方式,但是至此还遗留了一个问题,我们的Service只有指定了新的进程名之后才会是远程调用,如果通过bindService 传递过来的IBinder对象是同进程的,那我们就不需要使用IBinder.transact进行内核通信了。我们知道同进程之间利用方法调用方式就可以做到通信。 我们在onServiceConnected打印IBinder类型,如果发现是远程调用,传给我们的 iBinder 是 BinderProxy 类型,BinderProxy是Binder的内部类一样实现了IBinder接口,他在native层会对应一个C++的BpBinder,BpBinder 最终会通过Binder驱动跟Server端通信。如果是本地调用,打印出的类型为Stub,说明本地调用时,onServiceConnected传过来的就是我们在Service的onBinde方法返回的Stub对象本身。基于这个原理,我们可以设计一个转换方法。
首先我们要怎么判断当前是远程调用还是同进程调用呢? 我们使用queryLocalInterface(DESCRIPTOR)方法,Binder中queryLocalInterface不会返回空,而在BinderProxy的实现中,queryLocalInterface返回为null。 Binder:
public IInterface queryLocalInterface(String descriptor) { if (mDescriptor != null && mDescriptor.equals(descriptor)) { return mOwner; } return null; }
mOwner是attachInterface方法传进来的接口本身,后面还会出现这个方法。 BinderProxy:
public IInterface queryLocalInterface(String descriptor) { return null; }
当发现是远程调用时我们创建上边的Proxy来代理跨进程通信过程。要是本地调用我们直接返回本地Stub对象。
public static IMyInterface asInterface(IBinder iBinder){ if ((iBinder == null)) { return null; } //获取本地interface IInterface iin = iBinder.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof IMyInterface))) { //不为空,说明是本地调用,直接强转后返回。 //IMyInterface封装了test0()、test1()两个方法,本地对象和Proxy都继承自接口 return ((IMyInterface)iin ); } //为null,远程调用,新建代理 return new Proxy(iBinder); }
把上面相关代码完善之后
public interface IBinderTest extends android.os.IInterface { /** * 本地Stub对象 */ public static abstract class Stub extends android.os.Binder implements IBinderTest { private static final java.lang.String DESCRIPTOR = "com.XXX.XXXX.IBinderTest"; public Stub() { //绑定DESCRIPTOR,并设置queryLocalInterface方法返回的mOwner this.attachInterface(this, DESCRIPTOR); } /** * 本地远程转换 */ public static IBinderTest asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof IBinderTest))) { return ((IBinderTest) iin); } return new IBinderTest.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } /** * 处理Client调用请求 */ @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { java.lang.String descriptor = DESCRIPTOR; switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(descriptor); return true; } case TRANSACTION_testVoidAidl: { data.enforceInterface(descriptor); this.testVoidAidl(); reply.writeNoException(); return true; } case TRANSACTION_testStringAidl: { data.enforceInterface(descriptor); java.lang.String _result = this.testStringAidl(); reply.writeNoException(); reply.writeString(_result); return true; } default: { return super.onTransact(code, data, reply, flags); } } } /** * 远程调用代理类 */ private static class Proxy implements IBinderTest { 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 void testVoidAidl() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_testVoidAidl, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } @Override public java.lang.String testStringAidl() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_testStringAidl, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } } /** * 调用函数code */ static final int TRANSACTION_testVoidAidl = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_testStringAidl = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } public void testVoidAidl() throws android.os.RemoteException; public java.lang.String testStringAidl() throws android.os.RemoteException; }
如果你用过AIDL并且看过AIDL生成的代码,你就会发现上面代码就是AIDL生成的。 换掉Service的调用
public class MyService extends Service { private String TAG = "MyService"; @Override public IBinder onBind(Intent intent) { return new MyBinder(); } class MyBinder extends IBinderTest.Stub{ @Override public void testVoidAidl() throws RemoteException { Log.d(TAG, "testVoidAidl"); } @Override public String testStringAidl() throws RemoteException { Log.d(TAG, "testStringAidl"); return "hello"; } } }
加载全部内容