多进程需求在我们开发中常有遇见,因而学习多进程的通信机制是非常有必要的。我们知道,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实现进程间通信