Android进阶知识(七):Binder连接池
笔者不要做一只搬运代码的无聊肥柴。(啪啪啪,啊,好疼)
为了介绍Binder连接池,笔者感觉还是得介绍Binder连接池的实现,不然就只有工作原理没有代码实现介绍,感觉过意不去(懒的话还是可以直接贴链接的,但想想还是自己写好了)。
一、Binder连接池的工作原理
在AIDL的实现中,其大致流程为:首先创建一个Service和一个AIDL接口,接着创建一个类继承自AIDL接口中的Stub类并实现Stub中的抽象方法,在Service的onBind方法中返回这个类对象,然后客户端就可以绑定服务端,建立连接后就可以访问远端服务端的方法。
那么考虑这样一种情况,当业务扩展到需要10个业务模块都需要使用AIDL来进行进程间通信时,如果按照一个业务模块对应一个Service进行处理,那么就会有10个服务运行,作为系统资源的Service,一旦Service过多就会使得应用过于臃肿。
为了减少Service的数量,将所有的AIDL放在同一个Service中去管理,就有了Binder连接池。Binder连接池的主要作用是将每个业务模块的Binder请求统一转发到远程Service中去执行,从而避免了重复创建Service的过程。
应用Binder连接池的整个工作机制为:每个业务模块创建自己的AIDL接口并实现此接口,这个时候不同业务模块之间是不能耦合的,所有实现细节都单独分开,然后向服务端提供自己的唯一标识和其对应的Binder对象;对于服务端来说,只需要一个Service即可,服务端提供一个queryBinder接口,这个接口能够根据业务模块的特征来返回相应的Binder对象,不同业务模块拿到对应Binder对象后即可进行远程方法调用。其工作原理如下。
二、Binder连接池的实现
为了能够更好的理解Binder连接池的工作原理,下面将对Binder连接池进行实现。
以下以两个业务模块为例,其服务端的实现原理图如下所示。
其中ISecurityCenter接口提供了加解密功能,代码如下所示。
// Declare any non-default types here with import statements
interface ISecurityCenter {
String encrypt(String content);
String decrypt(String password);
}
而ICompute接口提供计算加法的功能,其声明代码如下所示。
// Declare any non-default types here with import statements
interface ICompute {
int add(int a, int b);
}
这两个AIDL接口的实现代码如下。
// ISecurityCenter.Stub实现
public class SecurityCenterImpl extends ISecurityCenter.Stub {
private static final char SECRET_CODE = '^';
@Override
public String encrypt(String content) throws RemoteException {
char[] chars = content.toCharArray();
for (int i = 0; i < chars.length; ++ i ) {
chars[i] ^= SECRET_CODE;
}
return new String(chars);
}
@Override
public String decrypt(String password) throws RemoteException {
return encrypt(password);
}
}
// ICompute.Stub实现
public class ComputeImpl extends ICompute.Stub {
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
}
接下来是为Binder连接池创建AIDL接口IBinderPool,其声明如下。
// Declare any non-default types here with import statements
interface IBinderPool {
IBinder queryBinder(int binderCode);
}
为Binder连接池创建远程Service并实现IBinderPool,注意这里不同于AIDL实现中为每个AIDL实现一个对应Service,而是仅仅为Binder连接池接口实现对应Service。queryBinder的具体实现中,可以看到请求转发的实现方法,当Binder连接池连接上远程服务时,会根据不同模块的标识即binderCode返回不同的Binder对象,通过这个Binder对象所执行的操作全部发生在服务端。Service的具体代码如下。
public class BinderPoolService extends Service {
private static final String TAG = BinderPoolService.class.getSimpleName();
private static final int BINDER_NONE = -1;
private static final int BINDER_COMPUTE = 0;
private static final int BINDER_SECURITY_CENTER = 1;
private Binder mBinderPool = new IBinderPool.Stub() {
@Override
public IBinder queryBinder(int binderCode) throws RemoteException {
IBinder binder = null;
switch (binderCode) {
case BINDER_SECURITY_CENTER:
binder = new SecurityCenterImpl();
break;
case BINDER_COMPUTE:
binder = new ComputeImpl();
break;
default:
break;
}
return binder;
}
};
public BinderPoolService() {
}
@Override
public IBinder onBind(Intent intent) {
return mBinderPool;
}
}
服务端的代码就是上述这样,客户端的代码实现原理如下所示。
首先,客户端需要一个连接服务端并且用于获取Binder对象的Binder连接池BinderPool,客户端可以通过它去绑定远程服务,绑定成功后,客户端就可以通过它的queryBinder方法获取各自对应的Binder对象,从而进行各自的操作。Binder连接池的代码如下所示。
public class BinderPool {
private static final String TAG = BinderPool.class.getSimpleName();
private Context mContext;
private IBinderPool mBinderPool;
@SuppressLint("StaticFieldLeak")
private static volatile BinderPool sInstance;
private CountDownLatch mCountDownLatch;
private BinderPool(Context context) {
mContext = context.getApplicationContext();
connectBinderPoolService();
}
public static BinderPool getInstance(Context context) {
if (sInstance == null) {
synchronized (BinderPool.class) {
if (sInstance == null) {
sInstance = new BinderPool(context);
}
}
}
return sInstance;
}
// 获取对应Binder对象
public IBinder queryBinder(int binderCode) {
IBinder binder = null;
try {
if (mBinderPool != null) {
binder = mBinderPool.queryBinder(binderCode);
}
} catch (RemoteException e) {
e.printStackTrace();
}
return binder;
}
private synchronized void connectBinderPoolService() {
mCountDownLatch = new CountDownLatch(1); // 实现同步
Intent service = new Intent(mContext, BinderPoolService.class);
mContext.bindService(service, mBinderPoolConnection, Context.BIND_AUTO_CREATE);
try {
mCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBinderPool = IBinderPool.Stub.asInterface(service);
try {
mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
mCountDownLatch.countDown();
}
@Override
public void onServiceDisconnected(ComponentName name) {
// ignored
}
};
// 断线重连机制
private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
Log.w(TAG, "binder died.");
mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
mBinderPool = null;
connectBinderPoolService();
}
};
}
在客户端中新建一个Activity,在线程中执行如下操作即可获取不同的Binder对象。
private void doWork() {
BinderPool binderPool = BinderPool.getInstance(MainActivity.this);
IBinder securityBinder = binderPool.queryBinder(BINDER_SECURITY_CENTER);
mSecurityCenter = ISecurityCenter.Stub.asInterface(securityBinder);
Log.d(TAG, "visit ISecurityCenter");
String msg = "helloworld-安卓";
Log.d(TAG, "content:" + msg);
try {
String password = mSecurityCenter.encrypt(msg);
Log.d(TAG, "encrypt:" + password);
Log.d(TAG, "decrypt:" + mSecurityCenter.decrypt(password));
} catch (RemoteException e) {
e.printStackTrace();
}
IBinder computeBinder = binderPool.queryBinder(BINDER_COMPUTE);
mCompute = ICompute.Stub.asInterface(computeBinder);
Log.d(TAG, "visit ICompute");
try {
Log.d(TAG, "3+5=" + mCompute.add(3, 5));
} catch (RemoteException e) {
e.printStackTrace();
}
}
额外说明一下,在Binder连接池中通过CountDownLatch将bindService这一异步操作转换成同步操作,这意味着其可能是耗时的,此外Binder方法的调用过程也可能是耗时的,因此需要在线程中执行。另外,BinderPool中有断线重连机制,当远程服务意外终止时,BinderPool会重新建立连接,这个时候如果业务模块中的Binder调用出现异常,也需要手动去重新获取最新的Binder对象。
参考资料:《Android开发艺术探索》