AIDL 与Binder

来源:互联网 发布:梦三国2mac版本 编辑:程序博客网 时间:2024/06/02 20:08

前段时间自己学习binder记录的一些文字, 如果有不对的地方, 麻烦指正, 以便能更进一步学习。


一、RPC(Remote process comunication), 进程间通讯;
       应用A欲访问或者获取服务端的数据, 可以通过AIDL来实现;
       首先新建RemoteService.aidl文件, 将自己想获取的数据以返回值或者入参形式传入接口函数, 如:
        interface RemoteService{
            String fun1();
            int fun2(in DemoClass demoObj); // 如果在eclipse中做, import会编译不过, 如果在Android源码中是可以的
            int fun3(inout DemoClass demoObj); // 如果在eclipse中做, import会编译不过, 如果在Android源码中是可以的
           int fun4(in byte[] inBuf, out byte[] outBuf, int len);
        }

        如果是在Android源码中, 编译后, out目录下可以找到对应的RemoteService.java文件, eclipse中编译则会在gen目录下自动生成对应RemoteService.java文件;
       RemoteService.java 说明:
         1.会有一个抽象内部类Stub, 是Binder的派生类, 实现RemoteService.aidl中的接口函数
            publicstaticabstractclassStubextendsandroid.os.Binderimplementscom.example.binderlearn.RemoteService            

     2.privatestaticfinaljava.lang.StringDESCRIPTOR= "com.example.binderlearn.RemoteService";
     DESCRIPTOR : 类描述字符串, 很重要, Binder绑定是通过这个描述字符串标志来通讯的;
      
     3.publicStub() {
          this.attachInterface(this,DESCRIPTOR);
      }
      抽象类的构造函数,attachInterface是Binder中的方法, 按函数说明看是联系接口和Binder的便捷方法;      
     
     4.publicstaticcom.example.binderlearn.RemoteService asInterface(android.os.IBinderobj){
        if((obj==null)) {
             returnnull;
         }
         android.os.IInterfaceiin=obj.queryLocalInterface(DESCRIPTOR);
        if(((iin!=null) && (iininstanceofcom.example.binderlearn.RemoteService))) {
              return((com.example.binderlearn.RemoteService)iin);
         }
        returnnewcom.example.binderlearn.RemoteService.Stub.Proxy(obj);
       }
       将IBinder对象转换成一个接口类型, 如果需要生成一个接口代理类;   
      
      5.publicandroid.os.IBinder asBinder() {
            returnthis;
       }
       返回一个IBinder对象, 此处是返回当前的Stub;
    
      6.publicbooleanonTransact(intcode, android.os.Parceldata,android.os.Parcelreply,intflags){
               switch(code){
                     caseTRANSACTION_fun1:{
                           data.enforceInterface(DESCRIPTOR);
                           java.lang.String_result=this.fun1();
                           reply.writeNoException();
                           reply.writeString(_result);
                           returntrue;
                     }
               }
               .....
       }
       其中code为每个接口函数对应的编码, data是对应函数传入的参数组成的一个数据包, reply为函数执行完成后返回的结果数据包;
      
      7.privatestaticclassProxyimplementscom.example.binderlearn.RemoteService {               
          privateandroid.os.IBindermRemote;          
          Proxy(android.os.IBinderremote) {
                 mRemote=remote;
          }

         @Override
         publicjava.lang.String fun1 () throws android.os.RemoteException {
                android.os.Parcel_data= android.os.Parcel.obtain();
          android.os.Parcel_reply= android.os.Parcel.obtain();
          java.lang.String_result;
         try{
              _data.writeInterfaceToken(DESCRIPTOR);
              mRemote.transact(Stub.TRANSACTION_fun1,_data,_reply, 0);
              _reply.readException();
              _result=_reply.readString();
          }finally{
               _reply.recycle();
               _data.recycle();
          }
         return_result;
          }
          ......
      }
      接口RemoteService 的代理类, 实现各接口函数;构造函数是在Stub.asInterface中调用, 将asInterface中的入参IBinder obj对象
      赋值给mRemote, mRemote是用于与Stub通讯;

     8.staticfinalintTRANSACTION_fun1 = (android.os.IBinder.FIRST_CALL_TRANSACTION+ 0);
       为接口函数编码, 将用于代理类与Stub.onTransact通讯的标识码;
        
         9.新建类publicclassRemoteServiceImplextends RemoteService.Stub
      是RemoteService.aidl生成的RemoteService.java中内部抽象类Stub的派生类, 完全实现接口方法,如:
       @Override
       publicString fun1() throws RemoteException {
              //TODOAuto-generated method stub
              return"AIDL 例子";
       }
     
     10.新建服务类
        publicclassMyServiceextendsService {
            privateStubserviceInfo= new RemoteServiceImpl();
            @Override
            publicIBinder onBind(Intent intent) {
              //TODOAuto-generated method stub
              returnserviceInfo;
            }
         }
         在onBind中返回一个RemoteService.Stub实列;注:需要在AndroidManifest.xml中注册该服务;
            
      11.在MainActivity的onCreate中新建Intent,设置意图的Action为10中的Myservice:
              IntentmIntent=new Intent();
              mIntent.setAction("com.example.binderlearn.MyService");
              mIntent.setPackage(getPackageName());
         然后bindService(mIntent,conn, Context.BIND_AUTO_CREATE);需要实列化conn, 其为ServiceConnection:

            ServiceConnectionconn=newServiceConnection(){
              @Override
              publicvoidonServiceConnected(ComponentNamename,IBinderservice) {
                           //TODOAuto-generated method stub
                    mRemoteService= RemoteService.Stub.asInterface(service);
               }

              @Override
              publicvoidonServiceDisconnected(ComponentNamename) {
                           
               }};
               mRemoteService定义:privateRemoteServicemRemoteService;其为接口类RemoteService对象;
                         
                         连接成功后mRemoteService实列化成功, 这样就可以调用mRemoteService.aidl中的函数了;
     二、整个调用流程
                 bindService(mIntent,conn, Context.BIND_AUTO_CREATE);这段代码是通过Binder将服务与接口绑定, 绑定服务成功后将会调用
          ServiceConnection中的回调函数,实例化接口类对象mRemoteService,调用接口函数fun1为例mRemoteService.fun1()数据走向是如下:
          
          bindService成功将调用onServiceConnected(ComponentNamenameIBinderservice), 其中入参service是MyService中的serviceInfo,
          mRemoteService= RemoteService.Stub.asInterface(service);这个将调用RemoteService.java中内部类Stub的asInterface方法, 
         mRemoteService将是接口RemoteService 的代理类, mRemoteService.fun1()将调用代理类中的fun1()方法,fun1()中
         mRemote.transact(Stub.TRANSACTION_fun1,_data,_reply, 0),
          它是一个native方法( frameworks/base/core/jni/android_util_Binder.cpp),里面进行了一系列的函数调用,它最终调用到了
       talkWithDriver函数, 看这个函数的名字就知道,通信过程要交给驱动完成了;这个函数最后通过ioctl系统调用,Client
              进程陷入内核态,Client调用add方法的线程挂起等待返回;驱动完成一系列的操作之后唤醒Server进程,调用了Server进
              程本地对象的onTransact函数,将会调用到RemoteService内部类Stub的onTransact,根据code代码,将走case TRANSACTION_fun1分支,
         分支中this.fun1()这段代码this是指向MyService中的serviceInfo,this.fun1()也即是调用RemoteServiceImpl中方法fun1(),
          也即达到跨进程调用了;

   三、bindService(Intent service, ServiceConnection conn,  int flags)
       Context.java中定义, 在ContextImpl.java中实现
       public boolean bindService(Intent service, ServiceConnection conn,int flags) {
          warnIfCallingFromSystemProcess();
          return bindServiceCommon(service, conn, flags, Process.myUserHandle());
       } 
        
            warnIfCallingFromSystemProcess是给出一个警告提示, Calling a method in the system process without a qualified user
           
            bindServiceCommon(Intent service, ServiceConnection conn, int flags,   UserHandle user)
       if (mPackageInfo != null) {
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
                    mMainThread.getHandler(), flags); // 这个过程会在主线程中进行
        } else {
            throw new RuntimeException("Not supported in system context");
        }
        validateServiceIntent(service);
        /* private void validateServiceIntent(Intent service) {
        if (service.getComponent() == null && service.getPackage() == null) {
            if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
                IllegalArgumentException ex = new IllegalArgumentException(
                        "Service Intent must be explicit: " + service);
                throw ex;
            } else {
                Log.w(TAG, "Implicit intents with startService are not safe: " + service
                        + " " + Debug.getCallers(2, 3));
            }
        }
       } //该函数是检查传进来的Intent是否合法,如果在onCreate中不mIntent.setPackage(getPackageName()); 这样设置将会报错:Service Intent must be explicit: */
         
        try {
            IBinder token = getActivityToken();
            if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
                    && mPackageInfo.getApplicationInfo().targetSdkVersion
                    < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                flags |= BIND_WAIVE_PRIORITY;
            }
            service.prepareToLeaveProcess(); // 把服务从应用的进程中移除掉
            int res = ActivityManagerNative.getDefault().bindService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, getOpPackageName(), user.getIdentifier());
            if (res < 0) {
                throw new SecurityException(
                        "Not allowed to bind to service " + service);
            }
            return res != 0;
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }

      四、图框描述
       

    五、注意事项


  • 在同进程中调用AIDL接口,AIDL接口代码的执行将在调用该AIDL接口的线程中完成,如果在主UI线程中调用AIDL接口,那么AIDL接口代码的执行将会在这个主UI线程中完成。如果是其他线程,AIDL接口代码的执行将在service中完成。因此,如果仅仅是本进程中的线程访问该服务,你完全可以控制哪些线程将访问这个服务(但是如果是这样,那就完全没必要使用AIDL了,而采取Binder接口的方式更为合适)。
  • 远程进程(其他线程)调用AIDL接口时,将会在AIDL所属的进程的线程池中分派一个线程来执行该AIDL代码,所以编写AIDL时,你必须准备好可能有未知线程访问、同一时间可能有多个调用发生(多个线程的访问),所以ADIL接口的实现必须是线程安全的。
  • 可以用关键字oneway来标明远程调用的行为属性,如果使用了该关键字,那么远程调用将仅仅是调用所需的数据传输过来并立即返回,而不会等待结果的返回,也即是说不会阻塞远程线程的运行。AIDL接口将最终将获得一个从Binder线程池中产生的调用(和普通的远程调用类似)。如果关键字oneway在本地调用中被使用,将不会对函数调用有任何影响。
  • 方法可以有0个或多个参数,可以使空返回值也可以返回所需的数据。
  • 所有非原始数据类型的参数必须指定参数方向(是传入参数,还是传出参数),传入参数使用in关键字标记,传出参数使用out,传入传出参数使用inout。如果没有显示的指定,那么将缺省使用in。
  • 在aidl文件中所有的注释都将会包含在生成的IBinder接口中(在Import和pacakge语句之上的注释除外)。
  • aidl中只支持成员方法,不支持成员变量。
  • java语言的原始数据类型(包括 int, long, char, boolen 等等)
  • String
  • CharSequence:该类是被TextView和其他控件对象使用的字符序列
  • List:列表中的所有元素必须是在此列出的类型,包括其他AIDL生成的接口和可打包类型。List可以像一般的类(例如List<String>)那样使用,另一边接收的具体类一般是一个ArrayList,这些方法会使用List接口
  • Map:Map中的所有元素必须是在此列出的类型,包括其他AIDL生成的接口和可打包类型。一般的maps(例如Map<String,Integer>)不被支持,另一边接收的具体类一般是一个HashMap,这些方法会使用Map接口。
  • 对于其他的类型,在aidl中必须使用import导入,即使该类型和aidl处于同一包内。
  • Binder是Android系统添加到Linux内核中的一个模块:Binder驱动,Linux原有内核中没有Binder, 有Socket、System V( 消息队列/共享内存/信号量)及管道;
  • serviceManagerNative.java最终实现是在:service_manager.c, 该服务是一开机就启动的, 是在init.rc中所设置; service_manager.c中的main函数是死循环(函数:binder_loop),binder进程监听(BINDER_WRITE_READ)是否有客户端申请访问server, 如有请求就调用回调函数svcmgr_handler;所有service是以链表的形式存储(service_manager.c 中svclist);SystemServer.java中startOtherServices()的ServiceManager.addService(String name, IBinder service), 将服务添加进serviceManager的链表中即相当于启动了服务, 当客户端想调用该服务, binder将会checkService, 如果有则可以RPC调用该service的方法;

原创粉丝点击