多进程需求在我们开发中常有遇见,因而学习多进程的通信机制是非常有必要的。我们知道,AIDL、Messenger等常用的进程间通信框架都是对Binder的封装,所以,学习Binder的使用对后续理解Binder机制有非常大的好处。本篇目的就是要从自定义Binder出发,实现基本的进程间通信功能。
我们的例子从一个加法服务出发,首先我们创建一个Service,该Service实现对其他进程传递来的两个参数做加法运算,并将运算结果进行返回。
public class MyService extends Service { private static final String TAG = "MyService"; @Override public IBinder onBind(Intent intent) { OLog.i(TAG, "onBind"); return myBinder; } Binder myBinder = new Binder() { @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { switch (code) { case RemoteProtocal.CODE_ADD: { int a = data.readInt(); int b = data.readInt(); int result = add(a, b); reply.writeInt(result); return true; } } return super.onTransact(code, data, reply, flags); } }; public int add(int a, int b) { return a + b; } }上面我们自定义了一个Binder,并重写了其onTransact方法,需要注意的是,onTransact有四个参数,其含义分别如下:
code: 标识码,用于区分操作,该值由Client端传递过来。
data: 是一个容器,用于包裹Client传递过来的参数数据
reply: 是一个容器,用于包裹返回给Client端的结果数据
flags: 表明是否有返回值,0表示有返回值,1表示没有返回值。该值在Server端无需做处理,而对于Client端来说需要进行指定。
这里Code自定义如下
public class RemoteProtocal { public static final int CODE_ADD = 1; }
onTransact方法中做了以下几步操作,首先判断code的取值是否是RemoteProtocal.CODE_ADD,是的话则说明Client端需要的是进行加法操作,进而下一步则是从data中获取由Client端传入的参数,这里实际上传进来的是两个int类型的数据,所以调用data的readInt方法分别获取到两个参数a,b,之后调用add方法求和,获取a+b的结果,并将结果写入到reply中,需要注意的是,方法最后还需要 return true。
这里,为了让确保我们的Service是运行在其他进程的,我们还需要在Mainifest中声明他的进程名,只需修改process属性
<service android:name=".server.MyService" android:enabled="true" android:exported="true" android:process=":remote" />
以上是Service的全部实现,接下来我们需要在Client端来绑定Service。
对于绑定一个Service,我们可以如下进行实现
// 绑定远程服务 private void bindRemoteService() { // 远程服务具体名称 ComponentName componentName = new ComponentName(this, "com.me.obo.mybinder.server.MyService"); Intent intent = new Intent(); intent.setComponent(componentName); // 绑定到服务 bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); } ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { // 持有binder的引用 mBinder = iBinder; tvConnectState.setText("Connected"); } @Override public void onServiceDisconnected(ComponentName componentName) {} };这块代码先是绑定了上面的Service,之后获取到Service返回的一个Binder的引用,该引用其实就是由Service里面返回的那个自定义Binder在Client端的映射对象。也就是说,操作这个iBinder对象,相当于操作Service里面的myBinder对象,我们将iBinder存在本地为mBinder。接下来,我们开始操作这个mBinder来达到使用远程服务的目的。
我们实现以下的方法
/** * 加法运算 * @param a * @param b */ private int add(int a, int b) { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); // 写入参数 a data.writeInt(a); // 写入参数 b data.writeInt(b); try { // 调用远程服务 mBinder.transact(RemoteProtocal.CODE_ADD, data, reply, 0); // 获取远程计算结果 int result = reply.readInt(); OLog.i(TAG, "result = " + result); return result; } catch (RemoteException e) { e.printStackTrace(); } return 0; }
这里我们通过Parcel.ontain()方法创建了data和reply两个Parcel类型的数据,并往data中写入了我们的a和b参数,之后直接调用mBinder的transact方法来调用远程Service的myBinder对象的onTransact方法,需要关注的是,transact的第一个参数我们传入的也是RemoteProtocal.CODE_ADD,需要与onTransact中的code对应起来,第四个参数传入了0,表示该方法是有返回值的。最后我们再从reply里面获取计算后的结果。至此,我们的一次完整的进程间通信工作完成。
测试工程界面如下:
完整工程代码:自定义Binder实现进程间通信