Android跨进程通信-Binder连接池的使用

本文介绍了Android中如何使用Binder连接池来优化多个业务模块间的AIDL通信,避免创建多个Service带来的资源消耗。详细讲解了Binder连接池的实现步骤,包括创建AIDL接口、实现Service和BinderPool,并提供了客户端的使用示例。
摘要由CSDN通过智能技术生成

1.为什么要有Binder连接池?

产生原因:因为当有多个不同的业务块都要使用AIDL来进行通信,则需要创建多个Service,每创建一个Service就需要消耗系统资源。

解决思路:将所有的AIDL放在一个Service中处理

2.Binder连接池的使用方法?

实现步骤: 

1. 首先,为每个业务模块创建AIDL接口并实现此接口及其业务方法。 
2. 创建IBinderPool的AIDL接口,定义IBinder queryBinder(int BinderCode)方法。外部通过调用此方法传入对应的code值来获取对应的Binder对象。 
3. 创建BinderPoolService,通过new BinderPool.BinderPoolImpl实例化Binder对象,通过onBind方法返回出去。 
4. 创建BinderPool类,单例模式,在构造方法中绑定Service,在onServiceConnected方法获取到BinderPoolImpl对象,这个BinderPoolImpl类是BinderPool的内部类,并实现了IBinderPool的业务方法。BinderPool类中向外暴露了queryBinder方法,这个方法其实调用的是BinderPoolImpl对象的queryBinder方法。

1、先提供两个AIDL接口来模拟多个模块都要使用AIDL的情况:ISpeak接口和ICalculate接口:

[java] view plain copy
 print?
  1. package com.chenyu.service;  
  2. interface ISpeak {  
  3.     void speak();  
  4. }  

[java] view plain copy
 print?
  1. package com.chenyu.service;  
  2. interface ICalculate {  
  3.     int add(in int num1,in int num2);  
  4. }  


接着,实现这两个接口:分别为Speak.java和Calculate.java文件:

[java] view plain copy
 print?
  1. public class Speak extends Stub {  
  2.     public Speak() {  
  3.         Log.d("cylog","我被实例化了..............");  
  4.     }  
  5.     @Override  
  6.     public void speak() throws RemoteException {  
  7.         int pid = android.os.Process.myPid();  
  8.         Log.d("cylog","当前进程ID为:"+pid+"-----"+"这里收到了客户端的speak请求");  
  9.     }  
  10. }  

[java] view plain copy
 print?
  1. public class Calculate extends ICalculate.Stub {  
  2.     @Override  
  3.     public int add(int num1, int num2) throws RemoteException {  
  4.         int pid = android.os.Process.myPid();  
  5.         Log.d("cylog""当前进程ID为:"+pid+"----"+"这里收到了客户端的Calculate请求");  
  6.         return num1+num2;  
  7.     }  
  8. }  


 
可以看到,这两个接口的实现类,都是继承了Interface.Stub类,这个在上一章的服务端代码出现过,是在服务端的service内部实现了接口的方法,而这里我们把实现了接口的方法从服务端抽离出来了,其实这个实现类依然是运行在服务端的进程中,从而实现了AIDL接口和服务端的解耦合工作,让服务端不再直接参与AIDL接口方法的实现工作。那么,服务端通过什么桥梁与AIDL接口联系呢?答案就是Binder连接池。Binder连接池管理着所有的AIDL接口,就如一位将军统帅着千军。客户端需要什么Binder,就提供信息给Binder连接池,而连接池根据相应信息返回正确的Binder,这样客户端就能执行特定的操作了。可以说,Binder连接池的思路,非常类似设计模式之中的工厂模式。接下来我们看Binder连接池的具体实现: 

2、为Binder连接池创建AIDL接口:IBinderPool.aidl:

[java] view plain copy
 print?
  1. interface IBinderPool {  
  2.     IBinder queryBinder(int binderCode);  //查找特定Binder的方法  
  3. }  

为什么需要这个接口?我们从上面的分析可以知道,service端并不直接提供具体的Binder,那么客户端和服务端连接的时候就应该返回一个IBinderPool对象,让客户端拿到这个IBinderPool的实例,然后由客户端决定应该用哪个Binder。所以服务端的代码很简单,只需要返回IBinderPool对象即可:


3、服务端service代码:

[java] view plain copy
 print?
  1. public class BinderPoolService extends Service {  
  2.   
  3.     private Binder mBinderPool = new BinderPool.BinderPoolImpl();   // 1  
  4.     private int pid = Process.myPid();  
  5.     @Override  
  6.     public IBinder onBind(Intent intent) {  
  7.         Log.d("cylog""当前进程ID为:"+pid+"----"+"客户端与服务端连接成功,服务端返回BinderPool.BinderPoolImpl 对象");  
  8.         return mBinderPool;  
  9.     }  
  10. }  

①号代码处,实例化了一个BinderPool.BinderPoolImpl类,并在onBind方法返回了这个mBinderPool对象。


4、接下来我们看BinderPool的具体实现,代码比较长,我们先大体上认识,再详细分析:

[java] view plain copy
 print?
  1. public class BinderPool {  
  2.     public static final int BINDER_SPEAK = 0;  
  3.     public static final int BINDER_CALCULATE = 1;  
  4.   
  5.     private Context mContext;  
  6.     private IBinderPool mBinderPool;  
  7.     private static volatile BinderPool sInstance;  
  8.     private CountDownLatch mConnectBinderPoolCountDownLatch;  
  9.   
  10.     private BinderPool(Context context) {<span style="white-space:pre">   </span>   // 1  
  11.         mContext = context.getApplicationContext();  
  12.         connectBinderPoolService();  
  13.     }  
  14.   
  15.     public static BinderPool getInstance(Context context) {     // 2  
  16.         if (sInstance == null) {  
  17.             synchronized (BinderPool.class) {  
  18.                 if (sInstance == null) {  
  19.                     sInstance = new BinderPool(context);  
  20.                 }  
  21.             }  
  22.         }  
  23.         return sInstance;  
  24.     }  
  25.   
  26.     private synchronized void connectBinderPoolService() {      // 3  
  27.         mConnectBinderPoolCountDownLatch = new CountDownLatch(1);  
  28.         Intent service = new Intent(mContext, BinderPoolService.class);  
  29.         mContext.bindService(service, mBinderPoolConnection,  
  30.                 Context.BIND_AUTO_CREATE);  
  31.         try {  
  32.             mConnectBinderPoolCountDownLatch.await();  
  33.         } catch (InterruptedException e) {  
  34.             e.printStackTrace();  
  35.         }  
  36.     }  
  37.       
  38.     public IBinder queryBinder(int binderCode) {          // 4  
  39.         IBinder binder = null;  
  40.         try {  
  41.             if (mBinderPool != null) {  
  42.                 binder = mBinderPool.queryBinder(binderCode);  
  43.             }  
  44.         } catch (RemoteException e) {  
  45.             e.printStackTrace();  
  46.         }  
  47.         return binder;  
  48.     }  
  49.   
  50.     private ServiceConnection mBinderPoolConnection = new ServiceConnection() {       // 5  
  51.   
  52.         @Override  
  53.         public void onServiceDisconnected(ComponentName name) {  
  54.               
  55.         }  
  56.   
  57.         @Override  
  58.         public void onServiceConnected(ComponentName name, IBinder service) {  
  59.             mBinderPool = IBinderPool.Stub.asInterface(service);  
  60.             try {  
  61.                 mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);  
  62.             } catch (RemoteException e) {  
  63.                 e.printStackTrace();  
  64.             }  
  65.             mConnectBinderPoolCountDownLatch.countDown();  
  66.         }  
  67.     };  
  68.   
  69.     private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {    // 6  
  70.         @Override  
  71.         public void binderDied() {  
  72.             mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);  
  73.             mBinderPool = null;  
  74.             connectBinderPoolService();  
  75.         }  
  76.     };  
  77.   
  78.     public static class BinderPoolImpl extends IBinderPool.Stub {     // 7  
  79.   
  80.         public BinderPoolImpl() {  
  81.             super();  
  82.         }  
  83.   
  84.         @Override  
  85.         public IBinder queryBinder(int binderCode) throws RemoteException {  
  86.             IBinder binder = null;  
  87.             switch (binderCode) {  
  88.                 case BINDER_SPEAK: {  
  89.                     binder = new Speak();  
  90.                     break;  
  91.                 }  
  92.                 case BINDER_CALCULATE: {  
  93.                     binder = new Calculate();  
  94.                     break;  
  95.                 }  
  96.                 default:  
  97.                     break;  
  98.             }  
  99.   
  100.             return binder;  
  101.         }  
  102.     }  
  103.   
  104. }  

大体上看,这个类完成的功能有实现客户端和服务端的连接,同时内有还有一个静态内部类:BinderPoolImpl,继承了IBinderPool.Stub,这也非常眼熟,所以这个静态内部类应该是运行了服务端的。好了,我们从上往下分析每一个方法的作用:

①private BinderPool(Context context)构造方法:这里传递了context对象,注意到,这个构造方法使用了private修饰,那么外界是无法直接调用构造器的,所以有了②号方法。


②public static BinderPool getInstance(Context context):看到getInstance字样,熟悉设计模式的读者应该知道了这里是使用了单例模式,而且是线程同步的懒汉式单例模式,在方法内部,把传递进来的context上下文参数传递进构造函数,即此时调用了①号方法,接着①号方法调用了connectBinderPoolService()方法,即③号方法。


③private synchronized void connectBinderPoolService():这个方法主要用于客户端与服务端建立连接,在方法内部出现了CountDownLatch类,这个类是用于线程同步的,由于bindService()是异步操作,所以如果要确保客户端在执行其他操作之前已经绑定好服务端,就应该先实现线程同步。这里简单提一下这个类:

CountDownLatch类有三个主要方法:

(1)构造方法 CountDownLatch(int num):这里传递一个num值,为countdownlatch内部的计时器赋值。

(2)countdown():每当调用一次这个方法,countdownlatch实例内部计时器数值 - 1。

     (3)await():让当前线程等待,如果内部计时器变为0,那么唤醒当前线程。


④public IBinder queryBinder(int binderCode):根据具体的binderCode值来获得某个特定的Binder,并返回。


⑤private ServiceConnection mBinderPoolConnection = new ServiceConnection(){} :这个类似于上一章客户端的连接代码,在服务端与客户端连接成功的时候,会回调当前的onServiceConnected()函数,我们来着重看看这个函数:

[java] view plain copy
 print?
  1. @Override  
  2.         public void onServiceConnected(ComponentName name, IBinder service) {  
  3.             mBinderPool = IBinderPool.Stub.asInterface(service);  
  4.             try {  
  5.                 mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);  
  6.             } catch (RemoteException e) {  
  7.                 e.printStackTrace();  
  8.             }  
  9.             mConnectBinderPoolCountDownLatch.countDown();  
  10.         }  

注意到,方法内部执行了mBinderPool = IBinderPool.Stub.asInterface(service)方法,由上一章的分析可知,这里的mBinderPool实际上是IBinderPool的一个代理对象,即此时客户端获得了服务端Binder连接池的一个代理对象。

接着,最后执行了mConnectBinderPoolCountDownLatch.countDown()方法,此时,执行bindService()的线程就会被唤醒。


⑥private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient(){}:为IBinder设置死亡监听,如果连接意外中断,会自动重新连接。


⑦public static class BinderPoolImpl extends IBinderPool.Stub{} :这个类实现了IBinderPool.Stub,内部实现了IBinderPool的接口方法,这个实现类运行在服务端。内部是queryBinder()方法的实现,根据不同的Bindercode值来实例化不同的实现类,比如Speak或者Calculate,并作为Binder返回给客户端。当客户端调用这个方法的时候,实际上已经是进行了一次AIDL方式的跨进程通信。

5、分析完BinderPool代码,最后,我们实现客户端代码:

[java] view plain copy
 print?
  1. package com.chenyu.binderpool;  
  2. import android.app.Activity;  
  3. import android.os.*;  
  4. import android.util.Log;  
  5.   
  6. import com.chenyu.service.BinderPool;  
  7. import com.chenyu.service.Calculate;  
  8. import com.chenyu.service.ICalculate;  
  9. import com.chenyu.service.ISpeak;  
  10. import com.chenyu.service.Speak;  
  11.   
  12. public class MainActivity extends Activity {  
  13.   
  14.     private ISpeak mSpeak;  
  15.     private ICalculate mCalculate;  
  16.     private int pid = android.os.Process.myPid();  
  17.     @Override  
  18.     protected void onCreate(Bundle savedInstanceState) {  
  19.         super.onCreate(savedInstanceState);  
  20.         setContentView(R.layout.activity_main);  
  21.         new Thread(new Runnable() {  
  22.             @Override  
  23.             public void run() {  
  24.                 startWork();  
  25.             }  
  26.         }).start();  
  27.     }  
  28.   
  29.     private void startWork() {  
  30.         Log.d("cylog","当前进程ID为:"+pid);  
  31.         Log.d("cylog","获取BinderPool对象............");  
  32.         BinderPool binderPool = BinderPool.getInsance(MainActivity.this);     // 1  
  33.         Log.d("cylog","获取speakBinder对象...........");  
  34.         IBinder speakBinder = binderPool.queryBinder(BinderPool.BINDER_SPEAK);  // 2  
  35.         Log.d("cylog","获取speak的代理对象............");  
  36.         mSpeak = (ISpeak) ISpeak.Stub.asInterface(speakBinder);    // 3   
  37.         try {  
  38.             mSpeak.speak();     // 4  
  39.         } catch (RemoteException e) {  
  40.             e.printStackTrace();  
  41.         }  
  42.         Log.d("cylog","获取calculateBinder对象...........");  
  43.         IBinder calculateBinder = binderPool.queryBinder(BinderPool.BINDER_CALCULATE);  
  44.         Log.d("cylog","获取calculate的代理对象............");  
  45.         mCalculate = (ICalculate) ICalculate.Stub.asInterface(calculateBinder);  
  46.         try {  
  47.             Log.d("cylog",""+mCalculate.add(5,6));  
  48.         } catch (RemoteException e) {  
  49.             e.printStackTrace();  
  50.         }  
  51.     }  
  52.   
  53. }  
由于跨进程通信是耗时操作,这里利用了子线程来进行绑定以及请求等操作。这里简单分析一下从子线程开始,整个跨进程通讯流程是怎样的:
首先,在①处,调用了BinderPool的getInstance()方法,在里面执行了绑定服务的操作,此时得到的binderPool是BinderPool对象。接着执行②号代码,调用BinderPool对象的queryBinder()方法,此时发生了AIDL跨进程请求,得到服务端返回的特定的IBinder对象。接着执行③号代码,调用ISpeak.Stub.asInterface(IBinder iBinder)方法,把刚才获得的IBinder对象传递进去,此时返回了Speak的Proxy代理对象。 最后执行④号代码,调用代理对象mSpeak的speak()方法,此时再次发生了AIDL跨进程请求,调用了服务端Speak类的speak方法。

我们看一下运行结果:



评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值