Android学习- 进程间通信之AIDL和Binder连接池

本文介绍了Android进程间通信机制AIDL的使用方法,包括创建服务端与客户端的基本流程及支持的数据类型。此外,还详细讲解了如何通过Binder池实现多个客户端与单一服务端之间的高效通信。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

作者:桂志宏

前面讨论过利用Messenger来进行进程间通信,这一节介绍另一种进程间通信的方法–AIDL

AIDL

AIDL的完整表述:Android Interface Definition Language 也就是android接口定义语言,它的本质是系统为我们提供的一种快速实现Binder的工具。

上面讲述利用Messenger来进行IPC通信,但是Messenger是以串行的方式来处理消息,如果有大量的并发请求,Messenger就不合适,另一方面,如果我们需要跨进程调用服务端的方法,Messenger就无法实现,因此这里需要借助AIDL来实现。

流程

我们先来看一下使用AIDL的流程,分为服务端和客户端。

对于服务端,首先需要创建一个Service,用来监听客户端的请求。然后创建AIDL文件,在AIDL文件中声明供客户端调用的接口,最后在Service中实现这个接口。

对于客户端,首先需要利用bindService来绑定Service,绑定成功后,利用asInterface将服务端返回的Binder转换为客户端需要的AIDL类型对象,接着就可以利用该对象来调用之前声明在AIDL文件中的方法,而该方法是运行在服务端的Binder线程池中。

上面的文字描述还是抽象,加入客户端需要在服务端计算矩阵特征值,因此我们需要创建IMatrixEgenValue.aidl文件,该文件的内容如下:

      interface IMatrixEgenValue{

                double egenValue(Matrix matrix);
        }

其中egenValue就是我们具体计算特征值的函数,我们需要在服务端实现它,这里有两种做法:1)新建一个Java类IMatrixEgenValueImpl去继承IMatrixEgenValue.Stub(Stub继承Binder),如下

         public class IMatrixEgenValueImpl extends IMatrixEgenValue.Stub{

                    @Override
                    public double egenValue(Matrix matrix) throw RemoteException{

                             ...//具体实现省略
                    }

       }

另一种做法是2)直接创建.Stub对象,如下

                     Binder binder=new IMatrixEgenValue.Stub(){


                      @Override
                    public double egenValue(Matrix matrix) throw RemoteException{

                             ...//具体实现省略
                    }

                  };

不管利用上面哪种方式实现,都要在服务端中获取IMatrixEgenValue.Stub对象,该对象是一个Binder,在Service的onBind方法中返回的就是IMatrixEgenValue.Stub对象。服务端代码如下(采用第一种方式实现egenValue):

          public class MatrixCaculationService extends Service{

            private Binder mBinder=new IMatrixEgenValueImpl();



                 @Override
                 public void onCreate(){
                       ...
                  }




                @Override
                public IBinder onBind(Intent intent){
                            ...

                        return mBinder;
                }



         }

上面就是服务端的流程。下面给出客户端的流程

在Activity中创建ServiceConnection对象,在onServiceConnected方法中利用服务端返回的Binder对象获得IMatrixEgenValue对象,然后就可以利用该对象调用egenValue方法,而该方法是运行在服务端Binder线程池中,这样就实现了客户端调用服务端方法,主要代码如下:

          public class MainActivity extends Activity{


                   ...

               private ServiceConnection mConnection=new ServiceConnection(){

                         @Override
                         public void onServiceConnected(ComponentName className,IBinder service){

                                    ...

                              new Thread(new Runnable(){

                                       @Override
                                       public void run() {

                                              ...
                                              IMatrixEgenValue matrixEgenValue=IMatrixEgenValue.Stub.asInterface(service);

                                              matrixEgenValue.egenValue(mMatrix);

                                                ...
                                       }

                               })

                                    ...


                           }


                  }







                @Override
                public void onCreate(Bundle savedInstanceState){
                           ...

                    Intent intent=new Intent(this,MatrixCaculationService.class);
                    bindService(intent,mConnection,Context.BIND_AUTO_CREATE);

                          ...

                 }



            }

这里需要指出的是,我们新开启了一个线程,然后在该线程中调用egenValue方法,其原因在于远程调用egenValue方法可能很耗时,因此不能在UI线程中调用。

以上就是AIDL最基本的使用流程,实际上AIDL远不止这么简单,需要进一步探索。

下面对AIDL的使用做一些说明:

1)支持的数据类型:

AIDL文件并不是支持所有数据类型,它所支持的数据类型有: 基本数据类型(int,long,char,boolean,double,float等), String, CharSequence, ArrayList(不是所有List都支持), HashMap(不是所有Map都支持), 所有实现Parcelable接口的自定义对象, AIDL(即AIDL接口本身也可以在AIDL文件中使用)

 (对象是不能直接在进程间传输,进程间对象的传输的本质就是反序列化,因此只有实现Parcelable的对象才能在进程间传输)

2)如果AIDL文件中使用了自定义的Parcelable对象,因此必须新建一个同名的AIDL文件,并且在文件中声明该Parcelable对象。

Binder连接池

前面讲述了利用AIDL来进行进程间通信的流程,如果存在很多的客户端都需要和服务端进行通信,难道要用AIDL一个一个建立服务?答案当然是否定的。 我们只用建立一个服务端,然后不同的业务模块向服务端提供唯一标识和其对应的Binder对象。其工作原理图如下:

接下来以一个具体的例子来说明。

加入公司需要开发三个业务模块,这三个业务模块互不影响,都需要和服务端进行通信,若这三个业务模块取名为ModuleA,ModuleB和ModuleC,因此我们先新建这三个业务模块的AIDL文件分别为ModuleA.AIDL, ModuleB.AIDL和ModuleC.AIDL。其代码如下:

  (ModuleA.AIDL) 

          interface ModuleA{

                 void doTaskA();  //ModuleA需要执行的任务逻辑

           }




    (ModuleB.AIDL)

           interface ModuleB{

                 void doTaskB();  //ModuleB需要执行的任务逻辑

           }



     (ModuleC.AIDL)

           interface ModuleC{

                 void doTaskC();  //ModuleC需要执行的任务逻辑

           }

接下来就要实现上面三个接口,新建ModuleAImpl,ModuleBImpl和ModuleCImpl三个java类,代码如下

      public class ModuleAImpl extends ModuelA.Stub{

              @Override
              public void doTaskA(){

                        ....
              }

      }


      //

      public class ModuleBImpl extends ModuelB.Stub{

              @Override
              public void doTaskB(){

                        ....
              }

      }




       //
       public class ModuleCImpl extends ModuelC.Stub{

              @Override
              public void doTaskC(){

                        ....
              }

      }

我们建立服务端BinderPoolService,代码如下:

        public class BinderPoolService extends Service{


           private Binder mBinderPool=new BinderPool.BinderPoolImpl(); 

           @Override
           public void onCreate(){
                super.onCreate();
           }


           @Override
           public IBinder onBind(Intent intent){
             return mBinderPool;
           }


        }

接下来就要实现Binder连接池,我们新建IBinderPool.AIDL文件,文件中声明获取相应Binder对象的方法,其代码如下:

     (IBinderPool.AIDL)

     interface IBinderPool{

         IBinder queryBinder(int binderCode);

     }

通过binderCode来确定返回上述三个业务中哪个业务模块的Binder对象。接下就是实现上述接口,我们新建一个BinderPool.java的class,不过我们不直接继承IBinderPool.Stub,而是选择在BinderPool内部新建一个内部类来继承IBinderPool.Stub并实现queryBinder方法。我们希望客户端获取BinderPool实例时就连接上服务端,客户端只用通过获取相应的Binder,并将其转化为相应的AIDL对象(还记得前面的asInterface方法吗),然后就可以直接调用运行在服务端的方法(注意,若方法耗时,需要新开一个Thread,然后在子线程中调用),因此我们在BinderPool的构造方法中需要连接服务,BinderPool的代码如下:

   (BinderPool.java)



    public class BinderPool{

          private Context mContext;

          private IBinderPool mBinderPool;

          //将sInstance声明为volatile是因为这里涉及到多线程并发访问服务端,需要对BinderPool对象加Lock
          private static volatile BinderPool sInstance;

          //先声明对应三个模块的binderCode,以便返回相应的Binder对象

          public static final int MODULEA_BINDER_CODE=0;

          public static final int MODUELB_BINDER_CODE=1;

          public static final int MODULEC_BINDER_CODE=2;




          //创建ServiceConnection实例
         private ServiceConnection mBinderPoolConnection=new ServiceConnection() {
                     @Override
                     public void onServiceConnected(ComponentName name, IBinder service) {
                         mBinderPool=IBinderPool.Stub.asInterface(service);
                     try{
                           /**这里给BinderPool设置死亡代理*/
                        mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient,0);
                     }catch(RemoteException e){
                         e.printStackTrace();
                     }


                }

               @Override
               public void onServiceDisconnected(ComponentName name) {




               }
};









       //接下提供获取BinderPool单例的方法,这里涉及到设计模式的内容

        //将构造器私有化
       private BinderPool(Context context){
         mContext=context.getApplicationContext();
          connectBinderPoolService(); //连接BinderPoolService
        }

      public static BinderPool getInstance(Context context){
          if (sInstance==null){
               synchronized(BinderPool.class){
                 if (sInstance==null){
                  sInstance=new BinderPool(context);
                 }
               }
         }

      return sInstance;
     }



       //连接Service
      private void connectBinderPoolService(){

        Intent service=new Intent(mContext, BinderPoolService.class);
        mContext.bindService(service,mBinderPoolConnection,Context.BIND_AUTO_CREATE);


        }




          //对Binder进行监听,当Binder死亡(一般是服务端进程意外停止)时,会回调binderDied方法,所以在这里重新连接Service
        private IBinder.DeathRecipient mBinderPoolDeathRecipient=new IBinder.DeathRecipient(){
            @Override
            public void binderDied(){
              Log.w(TAG,"binder died");
              mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient,0);
              mBinderPool=null;
              connectBinderPoolService();
             }
        };



        /**通过BinderPool对象获取相应的Binder对象,这里借助BinderPoolImpl的queryBinder*/
      public IBinder queryBinder(int binderCode){

                IBinder binder=null;

                try{
                 if(mBinderPool!=null){
                           binder=mBinderPool.queryBinder(binderCode);
                    }

                }catch(RemoteException e){
                     e.printStackTrace();
                }

              return binder;


      }



      //实现IBinderPool接口
       public static class BinderPoolImpl extends IBinderPool.Stub{


                public IBinder queryBinder(int binderCode){

                       IBinder binder=null;


                       switch(binderCode){
                           case MODULEA_BINDER_CODE:
                                binder=new ModuleAImpl()();
                                break;
                           case MODULEB_BINDER_CODE:
                                binder=new ModuleBImpl()();
                                break;
                           case MODULEC_BINDER_CODE:
                                binder=new ModuleCImpl();
                                break;
                           default:
                                break;
                        }

                     return binder;


              }


       }  



   }

上面是BinderPool的完整代码,这里梳理一下流程,当我们在客户端获取BinderPool实例时,这时要建立和服务端的连接,因此调用bindService,相应的就要创建ServiceConnection对象,当服务连接后,就要将服务端返回的Binder装换为IBinderPool.AIDL接口的对象mBinderPool,然后为了保证服务的健壮,当Binder死亡时能够自动重新连接服务,因此需要给Binder设置死亡代理,因此牵扯出DeathRecipient,进而创建DeathRecipient对象,重写binderDied方法,在该方法中重新连接Service。当我们在客户端得到BinderPool实例后,接下来就要获取相应的Binder对象,因此在BinderPool类中需要借助对象mBinderPool的queryBinder方法返回相应的Binder对象。获取Binder后,接下就要根据相应的模块将Binder转换为相应AIDL接口对象,然后利用该对象就可以远程调用远程方法。 我们以客户端执行模块B的任务为例,客户端的代码如下:

             ......

           BinderPool binderPool=BinderPool.getInstance(MainActivity.this);
           Binder binder=binderPool.queryBinder(BinderPool.MODULEB_BINDER_CODE);
           ModuleB moduleB=ModuleBImpl.asInterface(binder);
           moduleB.doTaskB();


           ......

上面调用代码最好放在子线程中执行。

至此,Binder连接池的原理和使用流程就介绍完毕了。但其实还有更多难点需要我们进一步探索

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值