Android跨进程通讯基础——Binder, BinderProxy, parcel, parcelable, Stub, Stub.Proxy(该文章最先于2013年6月7日发表于有道云笔记 进入阅读)java
百度、google 过不少文章,都没能找到可以从 API 使用者角度简单描述 Binder,BinderProxy,Parcel,Parcelable,Stub,Stub.Proxy 之间关系的文章,要么高深莫测,要么混乱不清。最终决定仍是本身动手,看源码,看文档,现总结以下:android
Binder,BinderProxy 造成了进程间通讯的基础,至关于公路桥梁;ide
Parcel 在 IBinder 基础上传输数据,至关于运输工具;工具
Parcelable 基本数据类型和实现了 Parcelable 接口的复合数据类型才可被 Parcel 传输,至关于摆放整齐、安检合格的货物;this
Stub,Stub.Proxy 实现跨进程调用的接口,至关于收发货方。google
注:Binder,BinderProxy 都实现了 IBinder 接口spa
下面以 Activity 与 Service 通讯为例来分析其运行机制。代理
示例目的:经过跨进程调用方式在 Activity 进程端调用 Service 进程端的方法。这些方法由接口 RemoteSSO 定义。这里假设二者是运行在不一样进程的(也能够运行在相同进程,具体因配置而不一样)。code
代码实现对象
一、定义 RemoteSSO.aidl 文件:
package com.sina.sso; interface RemoteSSO { String getPackageName(); String getActivityName(); }
二、ADT会在gen目录下生成RemoteSSO.java文件以下:
/* * This file is auto-generated. DO NOT MODIFY. * Original file: D:\\workspace_android\\IeltsPraBook\\src\\com\\sina\\sso\\RemoteSSO.aidl */ package com.sina.sso; public interface RemoteSSO extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.sina.sso.RemoteSSO { private static final java.lang.String DESCRIPTOR = "com.sina.sso.RemoteSSO"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.sina.sso.RemoteSSO interface, * generating a proxy if needed. */ public static com.sina.sso.RemoteSSO asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.sina.sso.RemoteSSO))) { return ((com.sina.sso.RemoteSSO)iin); } return new com.sina.sso.RemoteSSO.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_getPackageName: { data.enforceInterface(DESCRIPTOR); java.lang.String _result = this.getPackageName(); reply.writeNoException(); reply.writeString(_result); return true; } case TRANSACTION_getActivityName: { data.enforceInterface(DESCRIPTOR); java.lang.String _result = this.getActivityName(); reply.writeNoException(); reply.writeString(_result); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.sina.sso.RemoteSSO { 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 java.lang.String getPackageName() 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_getPackageName, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public java.lang.String getActivityName() 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_getActivityName, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_getPackageName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_getActivityName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } public java.lang.String getPackageName() throws android.os.RemoteException; public java.lang.String getActivityName() throws android.os.RemoteException; }
三、定义 MyRemoteSSO extends RemoteSSO.Stub。因为 RemoteSSO.Stub 是抽象类,咱们应该继承该类并实现相应的功能。这里是如下两方法:
public java.lang.String getPackageName() throws android.os.RemoteException; public java.lang.String getActivityName() throws android.os.RemoteException;
四、在 Service 的 onBind() 方法里面返回 MyRemoteSSO 对象。
五、在 Activity 中调用 bindService(),并在链接后经过如下转换就能够与另外一个进程中的 Service 通讯,调用相关方法了。
ServiceConnection mServConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder binder) { RemoteSSO mInterface = RemoteSSO.Stub.asInterface(binder); ... mInterface. getPackageName(); mInterface. getActivityName(); ... } }
至此,通道构造完毕,能够进行跨进程通讯了。
运行机制
一、绑定 Service,创建跨进程通讯通道
a、Activity 中调用 context.bindService(),Android 经过系统服务来启动或者链接到 Service;
b、系统服务通知 Service 回调 onBind() 方法返回 Binder 对象的一个实例(Binder 对象事实上是 RemoteSSO.Stub 的子类对象,而 RemoteSSO.Stub 继承自 Binder);
c、系统服务通知 Activity 进程端生成一个对应的 BinderProxy 对象,并做为 onServiceConnected(ComponentName name, IBinder binder) 方法的回调参数 binder,此时咱们须要将 binder 做为参数手动调用 RemoteSSO.Stub. asInterface(binder) 来获取一个实现了 RemoteSSO 接口的代理对象 proxy 以供咱们调用,此时通道创建完成,咱们能够经过 proxy 来调用 Service 端的方法了。
因为 proxy 是实现了自定义接口 RemoteSSO 的,所以外部看来只是调用了接口方法而不是代理。事实上,proxy 是一个 RemoteSSO.Stub.Proxy 对象的实例,参数 binder(BinderProxy对象实例)便做为了 proxy 的一个变量备用。
Binder 和 BinderProxy 是成对的,在进程间用 descriptor 来标记对应关系,descriptor 一般是 RemoteSSO 的全路径名,在自动生成的 RemoteSSO.java 接口中已经自动生成。同时 Stub 和 Stub.Proxy 也是成对的,Service 端返回的是继承了 Binder 的 Stub 的子类对象实例,而 Activity 端用来实现调用的接口实例是将 BinderProxy 做为变量的 Stub.Proxy 对象的实例。
二、调用与返回
毕竟,咱们的最终目的是通讯,这里就是调用方法。那么当咱们拿到 RemoteSSO 接口的实例 proxy,调用 getPackageName() 的时候,执行过程是怎样的呢?
a、因为 proxy 是 RemoteSSO.Stub.Proxy 对象的实例,则 proxy.getPackageName() 即是 RemoteSSO.Stub.Proxy 的 getPackageName()。
@Override public java.lang.String getPackageName() 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_getPackageName, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; }
该代码是自动生成的。注意这里的 mRemote 就是 BinderProxy。
b、代理对象 RemoteSSO.Stub.Proxy 将方法的参数打包到 Parcel 对象的实例 _data 里,并由 BinderProxy 调用 transact()(见 mRemote.transact(Stub.TRANSACTION_getPackageName, _data, _reply, 0); )经过底层系统级跨进程机制通知 Service 端的 Binder(Service.onBind() 返回的 RemoteSSO.Stub 子类对象)调用 transact(int code, Parcel data, Parcel reply, int flags);注意方法名是经过 code 参数标记的,见常量 Stub.TRANSACTION_getPackageName。
c、Binder.transact() 回调 RemoteSSO.Stub 的 onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags),进而调用自定义接口方法,并将返回值写入到 reply 进行打包并返回。这里是 getPackageName(),该自定义方法是手动在 RemoteSSO.Stub 的子类中实现的(具体要作什么只有本身知道,ADT 不可能帮你生成)。
@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_getPackageName: { data.enforceInterface(DESCRIPTOR); java.lang.String _result = this.getPackageName(); reply.writeNoException(); reply.writeString(_result); return true; } case TRANSACTION_getActivityName: { data.enforceInterface(DESCRIPTOR); java.lang.String _result = this.getActivityName(); reply.writeNoException(); reply.writeString(_result); return true; } } return super.onTransact(code, data, reply, flags); }
注意这里根据 code 对方法名的标记找到对应的实际要调用的方法。
d、Binder.transact()、BinderProxy.transact() 一层层返回,即 RemoteSSO.Stub.Proxy.getPackageName() 方法的 mRemote.transact(Stub.TRANSACTION_getPackageName, _data, _reply, 0) 方法返回,此时 _reply 已经在 Service 进程中被填充了返回值,接下来能够读取该值并返回了。
至此,整个调用过程结束。
FAQ
你们有没有想过,若是调用发生在同一个进程会发生什么?
public void onServiceConnected(ComponentName name, IBinder binder) { RemoteSSO mInterface = RemoteSSO.Stub.asInterface(binder); ... } public static com.sina.sso.RemoteSSO asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.sina.sso.RemoteSSO))) { return ((com.sina.sso.RemoteSSO)iin); } return new com.sina.sso.RemoteSSO.Stub.Proxy(obj); }
事实上,在同一个进程中 onServiceConnected() 的参数 binder 是 Binder 对象而不是 BinderProxy 对象,所以在执行 asInterface() 的时候返回的是 binder 自己(见obj.queryLocalInterface(DESCRIPTOR);),也就是 RemoteSSO.Stub 子类对象,而不是 RemoteSSO.Stub.Proxy 对象,此时在执行 getPackageName() 时直接就是调用手动实现的该方法,也就是直接调用了 Service 端的该方法,而没有绕一个大圈。若是必然是在同一个进程,能够直接这样处理来得到调用接口:
public void onServiceConnected(ComponentName name, IBinder binder) { RemoteSSO mInterface = (RemoteSSO)binder; ... }