在前几篇介绍过如何使用 Messenger、AIDL、ContentProvider 和 Socket 进行 IPC,这一篇介绍一种客户端请求若干个服务端对象并且调用各个业务模块的方法的例子,还有总结一下我们学过的这么多种 IPC 方式。
1. 使用 Binder 连接池
Binder 连接池可以用于一个服务端提供多个业务功能模块给客户端调用。简单的思路就是:服务端 Service 提供一个查询功能的 AIDL 接口( IBinderPool.AIDL ),客户端连接服务端后获取该 AIDL 的代理对象,然后传特定的参数获取对应的服务端中准备好的业务模块的 AIDL 对象(其实就是 Binder 对象)。
- 建立服务端
服务端新建各种业务模块的 AIDL 接口和提供给客户端的统一查询接口 ,然后服务端分别实现所有的 AIDL 接口(继承所有 AIDL 接口的 Stub
类),服务端新建一个 Service 提供给客户端连接。
服务端的源码:https://github.com/innovatorCL/IPCServer
show my code
业务模块接口
// ICompute.aidl
package com.innovator.ipcclient;
//计算加法功能
interface ICompute {
int add(int a,int b);
}
// ISecurityCenter.aidl
package com.innovator.ipcclient;
// 加解密功能
interface ISecurityCenter {
String encrypt(String content);
String decript(String password);
}
开放给客户端的统一查询接口
// IBinderPool.aidl
package com.innovator.ipcclient;
// 对外提供给客户端,并且提供选择 Binder 的方法
interface IBinderPool {
IBinder queryBinder(int binderCode);
}
服务端各个业务模块的实现
/**
* ICompute AIDL 的实现类
* Created by innovator on 2018/2/11.
*/
public class ComputeImpl extends ICompute.Stub{
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
}
/**
* 实现 ISecurityCenter AIDL 接口的业务类
* Created by innovator on 2018/2/11.
*/
public class SecurityCenterImpl extends ISecurityCenter.Stub{
public 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 decript(String password) throws RemoteException {
return encrypt(password);
}
}
服务端 Servcie
/**
* BinderPool 的服务端
* Created by innovator on 2018/2/13.
*/
public class BinderPoolService extends Service {
private static final String TAG = "BinderPool";
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG,"BinderPool Service onCreate");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG,"BinderPool Service onDestroy");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
//返回对外的统一 AIDL 接口,有点像工厂方法
Log.i(TAG,"onBind");
return new BinderPoolImpl();
}
public static class BinderPoolImpl extends IBinderPool.Stub{
public BinderPoolImpl(){
super();
}
@Override
public IBinder queryBinder(int binderCode) throws RemoteException {
IBinder binder = null;
//根据不同的请求 Code 返回不同的 Binder 对象
switch (binderCode){
case BINDER_SECURITY_CENTER:
binder = new SecurityCenterImpl();
break;
case BINDER_COMPUTE:
binder = new ComputeImpl();
break;
default:
break;
}
return binder;
}
}
}
- 建立客户端
客户端在 Activity 中连接服务端的 Service,获取服务端的 IBinderPool.Stub 对象
,然后根据需要获取对应的业务类型对象。这里的 IBinderPool.Stub 对象
有点像一个中转站,类似设计模式的工厂方法模式。
客户端的源码:https://github.com/innovatorCL/IPCClient
show my code
封装访问服务端的工具类
/**
* 封装好用来访问服务端的工具
* Created by innovator on 2018/2/13.
*/
public class BinderPool {
private static final String TAG = "BinderPool";
private static final int BINDER_NONE = -1;
public static final int BINDER_SECURITY_CENTER = 3;
public static final int BINDER_COMPUTE = 4;
private Context mContext;
private IBinderPool mBinderPool;
private static volatile BinderPool sInatance;
private CountDownLatch mCountDownLatch;
private BinderPool(Context context){
this.mContext = context;
connectBinderPoolService();
}
public static BinderPool getsInatance(Context context){
if(sInatance == null){
synchronized (BinderPool.class){
if(sInatance == null){
sInatance = new BinderPool(context);
}
}
}
return sInatance;
}
private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBinderPool = IBinderPool.Stub.asInterface(service);
try{
mBinderPool.asBinder().linkToDeath(mDeathRecipient,0);
}catch (RemoteException e){
Log.i(TAG,"连接 BinderPool 出错:"+e.getMessage());
}
Log.i(TAG,"mCountDownLatch.countDown: "+Thread.currentThread().getName());
// Binder 回调在主线程
mCountDownLatch.countDown();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
Log.i(TAG,"binder dead");
mBinderPool.asBinder().unlinkToDeath(mDeathRecipient,0);
mBinderPool = null;
connectBinderPoolService();
}
};
/**
* 连接 BinderPool 服务端
*/
private synchronized void connectBinderPoolService() {
mCountDownLatch = new CountDownLatch(1);
Intent i = new Intent();
i.setAction("com.innovator.binderpool");
i.setPackage("com.innovator.ipcserver");
mContext.bindService(i,mBinderPoolConnection, Context.BIND_AUTO_CREATE);
Log.i(TAG,"bindService");
try {
Log.i(TAG,"mCountDownLatch.await: "+Thread.currentThread().getName());
mCountDownLatch.await();
}catch (Exception e){
Log.i(TAG,"mCountDownLatch.await 出错:"+e.getMessage());
}
}
/**
* 客户端根据业务需求传递不同的 Code 获取对应的 Binder 对象
* @param code
* @return
*/
public IBinder getServerBinderByCode(int code){
IBinder binder = null;
try {
if(mBinderPool != null){
binder = mBinderPool.queryBinder(code);
}
}catch (RemoteException e){
Log.i(TAG,"mBinderPool.queryBinder 出错:"+e.getMessage());
}
return binder;
}
}
在 Activity 中连接服务端
/**
* BinderPoolActivity,Binder 连接池的客户端
*/
public class BinderPoolActivity extends AppCompatActivity {
private static final String TAG = "BinderPool";
private ISecurityCenter securityCenter;
private ICompute compute;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_binder_pool);
}
public void doMyWork(View view){
//因为 CountDownLatch 是阻塞的,而且连接 Binder 是耗时的,所以放在子线程去做
new Thread(new Runnable() {
@Override
public void run() {
doWork();
}
}).start();
}
public void doWork(){
BinderPool binderPool = BinderPool.getsInatance(BinderPoolActivity.this);
IBinder securityBinder = binderPool.getServerBinderByCode(BINDER_SECURITY_CENTER);
securityCenter = ISecurityCenter.Stub.asInterface(securityBinder);
Log.i(TAG,"visit ISecurityCenter:"+Thread.currentThread().getName());
String msg = "大爱安卓";
try{
String password = securityCenter.encrypt(msg);
Log.i(TAG,"encrypt: "+ password);
password = securityCenter.decript(password);
Log.i(TAG,"decript: "+ password);
}catch (Exception e){
Log.i(TAG,"msecurityCenter 加解密出错:"+e.getMessage());
}
IBinder computeBinder = binderPool.getServerBinderByCode(BINDER_COMPUTE);
compute = ICompute.Stub.asInterface(computeBinder);
try{
Log.i(TAG,"compute 计算 4+6: "+ compute.add(4,6));
}catch (Exception e){
Log.i(TAG,"compute 计算出错:"+e.getMessage());
}
}
}
- 结果
客户端打印的 Log
客户端打印的 Log通过 BinderPool
成功调用服务端各个业务模块接口功能。
网友评论