Android知识点总结(四)进程间通信
上一篇文章记录了Messenger及AIDL的使用。其中AIDL的使用是最广泛的,其大致流程:
- 首先创建一个Service和一个AIDL接口
- 接着创建一个类继承自AIDL接口中的Stub类并实现Stub中的抽象方法(也可以直接使用匿名内部类的方式实现),在Service的onBind方法中返回这个类的对象
- 然后客户端就可以绑定服务端Service,建立连接后就可以访问远程服务端的方法了。
但是如果项目业务越来越大,需要10个AIDL来进行进程间通信时,创建10个Service显然不太现实,这时就需要Binder连接池技术,该模式工作机制是这样的:
- 每个业务模块创建自己的AIDL接口
- 同时提供一个Binder池接口,提供queryBinder,这个接口能根据业务需求,返回对应的Binder对象,这样不同业务模块就能根据自己的Binder进行远程调用
- 此时只需一个Service,在其onBind方法里返回Binder池对应的Binder类,客户端就能获取不同的Binder实现所需功能。
下面写一个例子,首先建立两个AIDL接口,模拟多个业务模块的情况:
// ISecurityCenter.aidl
package com.scy.component.testlaunteractivity;
// Declare any non-default types here with import statements
interface ISecurityCenter {
String encrypt(String content);
String decrypt(String psd);
}
// ICompute.aidl
package com.scy.component.testlaunteractivity;
// Declare any non-default types here with import statements
interface ICompute {
int add(int a, int b);
}
接着创建类继承各自AIDL接口中的Stub类并实现Stub中的抽象方法:
public class SecurityCenterImpl extends ISecurityCenter.Stub{
private static final char SECRET = '*';
@Override
public String encrypt(String content) throws RemoteException {
char[] chars = content.toCharArray();
for (int i = 0; i < chars.length; i++) {
chars[i] ^= SECRET;
}
return new String(chars);
}
@Override
public String decrypt(String psd) throws RemoteException {
return encrypt(psd);//两次异或算法达到解密效果
}
}
public class IComputeImpl extends ICompute.Stub {
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
}
接下来为Binder连接池创建AIDL接口,并创建类继承接口实现其Stub:
// IBinderPool.aidl
package com.scy.component.testlaunteractivity;
// Declare any non-default types here with import statements
interface IBinderPool {
IBinder queryBinder(int code);
}
public class IBinderPoolImpl extends IBinderPool.Stub {
@Override
public IBinder queryBinder(int code) throws RemoteException {
IBinder binder = null;
switch (code){
case 0:
binder = new SecurityCenterImpl();
break;
case 1:
binder = new IComputeImpl();
break;
default:break;
}
return binder;
}
}
Service比较简单:
public class BinderPoolService extends Service {
public BinderPoolService() {
}
@Override
public IBinder onBind(Intent intent) {
return new IBinderPoolImpl();
}
}
下面是Binder连接池的具体实现,在它的内部实现要去绑定远程服务,绑定成功后,客户端就可以通过它的queryBinder方法,获取各自对应的Binder进行各自的操作了。
public class BinderPool {
public static final int BINDER_COMPUTE = 1;
public static final int BINDER_SECURITY = 0;
public static final int BINDER_NONE = -1;
private Context context;
private IBinderPool mBinderPool;
private static volatile BinderPool mInstance;
private CountDownLatch mCountDownLatch;
public BinderPool(Context context) {
this.context = context.getApplicationContext();
connectService();
}
public static BinderPool getInstance(Context context) {
if (mInstance == null) {
synchronized (BinderPool.class) {
if (mInstance == null) {
mInstance = new BinderPool(context);
}
}
}
return mInstance;
}
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
mBinderPool.asBinder().unlinkToDeath(mDeathRecipient, 0);
mBinderPool = null;
connectService();
}
};
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBinderPool = IBinderPool.Stub.asInterface(service);
try {
mBinderPool.asBinder().linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
//计时器的值减一 变为0 mCountDownLatch.await()造成的阻塞解除 一般放到finally语句执行
mCountDownLatch.countDown();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private synchronized void connectService() {
Log.e("--==", "connect.....");
//限定闭锁需要等待的线程数量
mCountDownLatch = new CountDownLatch(1);
Intent intent = new Intent(context, BinderPoolService.class);
context.bindService(intent, connection, Context.BIND_AUTO_CREATE);
try {
//阻塞当前线程,直到计时器的值为0
mCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public IBinder queryBinder(int binderCode) {
IBinder binder = null;
try {
if (mBinderPool != null) {
binder = mBinderPool.queryBinder(binderCode);
}
} catch (RemoteException e) {
e.printStackTrace();
}
return binder;
}
}
最后可以去客户端验证一下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
try {
doWork();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}).start();
}
private void doWork() throws RemoteException {
BinderPool binderPool = BinderPool.getInstance(this);
IBinder securityBinder = binderPool.queryBinder(0);
ISecurityCenter iSecurityCenter = SecurityCenterImpl.asInterface(securityBinder);
String psd = iSecurityCenter.encrypt("我爱你中国");
String decrypt = iSecurityCenter.decrypt(psd);
Log.e("--==", psd + " -- " + decrypt);
IBinder computeBinder = binderPool.queryBinder(BinderPool.BINDER_COMPUTE);
ICompute iCompute = IComputeImpl.asInterface(computeBinder);
int num = iCompute.add(3, 5);
Log.e("--==", "" + num);
}
}
输出日志如下:
04-11 14:33:22.207 13310-13335/com.scy.component.testlaunteractivity E/--==: connect.....
04-11 14:33:22.281 13310-13335/com.scy.component.testlaunteractivity E/--==: 戻爛佊万囗 -- 我爱你中国
04-11 14:33:22.283 13310-13335/com.scy.component.testlaunteractivity E/--==: 8
在Binder连接池的实现中,我们通过CountDownLatch将bindService这一异步操作同步化,这就意味着它是耗时的,然后就是Binder方法的调用也可能是耗时的,因此不建议在主线程中去执行。
如果业务模块需要新加一个AIDL,那么在它实现了自己的AIDL接口后,只需修改BinderPoolImpl中的queryBinder方法,添加一个新的binderCode并返回对应的Binder对象即可。