Android跨进程通信----AIDL使用方法

1,什么是AIDL

全称为Android interface Definition Language即Android接口定义语言.

Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同的进程之间进行通信。为了使其他的应用程序也可以访问到本应用程序提供的服务,Android系统采用远过程调用方式来实现。与很多其他的基于RPC的解决方案一样,Android使用一种借口定义语言来公开服务的接口。我们知道4个Android应用程序组件中的3个(Activity、BroadcastReceiver和ContentProvider)都可以进行跨进程通信,另外一个Android应用程序组件Service同样可以。因此,可以将这种跨进程访问的服务称为AIDL服务。


2,什么时候Service是跨进程的,什么时候它不是跨进程的?

举个例子,当我们在一个应用里,写了两个Activity:Activity1.java、Activity2.java,写了一个Service:Service1.java。我在Activity1的Context中使用startActivity(Intent)起了Activity2,这是不是跨进程了?答案是可能是,也可能不是。当我们在应用的AndroidManifest.XML文件中为activity或Service指定了android:process=”…”属性以后,我们就可以让对应的Activity和Service运行在指定的进程中。如果我们不指定android:process属性,那么它们都运行在默认以应用包名为进程名的对应进程中。关于这个android:process属性的介绍,可以查到很多资料,这里不细说了,就是要有这个意识,不一定同一个应用中的组件都运行在同一个process里。
OK,那不同应用的组件之间一定运行在不同进程中吗?一般情况下是这样的,但我也不能说的太绝对,因为有些特殊情况下不同应用的组件会运行在同一个进程中,这是后话,一般可以默认不同应用是运行在不同进程中的。
清楚了这一点后,我们抛出下一个梗,我想在运行于process A的Activity中调用运行于process B的Service的一些对象方法,怎么搞?OK,有个办法是广播。你们互相发广播吧,这是可以的,我们需要定义一系列的广播。举个具体点的例子吧,否则说起来真不方便:假设有一个Service叫做“天气助手”,里面有一个类叫WeatherAnswer。这个WeatherAnswer中定义了一系列的方法,比如getDay(),getTemp(),getCity(),getTemp(Int)…….那么一大串的方法下来,我们使用广播显然有点儿吃力啊,我们要在Service的广播接收器中为每个方法定义一个广播项,然后还要判断解析出传入的参数来,这显然是个不科学的用法。广播更多的时候是被用来处理一些消息,而传递数据这件事不该过于依赖广播。那该怎么办呢?什么ContentProvider之流的跨进程手段更是不济,此时AIDL就呼之欲出了。
在这呼之而出之前有个问题需要在此时抛出来:如果该Service和我们的Activity是在同一个进程里的话,同样的需求该如何实现?这个问题同样可以由AIDL来解决,并且对于应用开发者来说,开发的步骤是一模一样的,也就是说AIDL对上层开发和底层机制的隔离做得非常人性化。
终于进入正题了,我们先来约定一下。我们的Service叫做WeatherService,它在后台跑着(先不管它是不是和Activity运行在同一个进程里),其中有一个叫做MyAIDLImpl的对象,专门提供天气预报应有的各种服务,它被当做跨进程通信的Server,顾名思义,它是提供服务的对象,所以是Server。然后我们要在一个MainActivity中使用MyAIDLImpl对象提供给我们的方法int getWeather(),float getTemp(int day),String getCity(),就先这三个方法吧,由于Activity里需要使用Server提供的服务,所以它是Client。
我们先来定义一下Server的接口,创建一个MyWeatherAIDL.aidl文件。在里面定义int getWeather(),float getTemp(int day),String getCity()三个接口,(我使用的eclipse,androidstudio也是类似的过程)在包里手动创建一个MyWeatherAIDL.aidl文件,然后用像写一个接口一样的语法定义方法:

package  com.example.hello ;

interface MyWeatherAIDL {
     int getWeather ( ) ;
     float getTemp ( int day ) ;
     String getCity ( ) ;
}

这样保存之后,eclipse为我们自动生成了对应的java文件(aidl也就是为了方便的告诉环境,我们要什么样的接口,然后接下来环境说,哦,是AIDL啊,那我自动帮你生成个java文件吧,这才是你真正要的,你自己写的话太累了,just这个目的)。生成的java文件位于/gen/com/example/hello下,打开来看看eclipse都为我们做了些什么:

/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: E:\\eclipse_workspace\\Hello\\src\\com\\example\\hello\

\MyWeatherAIDL.aidl
*/

package  com.example.hello ;

public  interface MyWeatherAIDL  extends android. os. IInterface
{
     /** Local-side IPC implementation stub class. */
     public  static  abstract  class  Stub  extends android. os. Binder  implements
        com. example. hello. MyWeatherAIDL {

         private  static  final java. lang. StringDESCRIPTOR  =  "com.example.hello.MyWeatherAIDL" ;

         /** Construct the stub at attach it to the interface. */
         public  Stub ( ) {
             this. attachInterface ( this, DESCRIPTOR ) ;
         }
         /**
        * Cast an IBinder object into an com.example.hello.MyWeatherAIDL interface,
        * generating a proxy if needed.
        */


         public  static com. example. hello. MyWeatherAIDL asInterface
             (android. os. IBinder obj ) {
             if  ( (obj == null ) )  {
                 return  null ;
             }
            android. os. IInterface iin  = obj. queryLocalInterface (DESCRIPTOR ) ;
             if  ( ( (iin != null ) &amp ;&amp ; (iin  instanceof 
                com. example. hello. MyWeatherAIDL ) ) ) {
                 return  ( (com. example. hello. MyWeatherAIDL )iin ) ;
             }
             return  new com. example. hello. MyWeatherAIDL. Stub. Proxy (obj ) ;
         }

        @Override
         public android. os. IBinder asBinder ( ) {
             return  this ;
         }

        @Override  public  boolean onTransact ( int code, android. os. Parcel data, 
            android. os. Parcel reply,  int flags )  throws android. os. RemoteException {
             switch  (code ) {
                 case INTERFACE_TRANSACTION :
                 {
                    reply. writeString (DESCRIPTOR ) ;
                     return  true ;
                 }
                 case TRANSACTION_getWeather :
                 {
                    data. enforceInterface (DESCRIPTOR ) ;
                     int _result  =  this. getWeather ( ) ;
                    reply. writeNoException ( ) ;
                    reply. writeInt (_result ) ;
                     return  true ;
                 }
                 case TRANSACTION_getTemp :
                 {
                    data. enforceInterface (DESCRIPTOR ) ;
                     int _arg0 ;
                    _arg0  = data. readInt ( ) ;
                     float _result  =  this. getTemp (_arg0 ) ;
                    reply. writeNoException ( ) ;
                    reply. writeFloat (_result ) ;
                     return  true ;
                 }
                 case TRANSACTION_getCity :
                 {
                    data. enforceInterface (DESCRIPTOR ) ;
                    java. lang. String _result  =  this. getCity ( ) ;
                    reply. writeNoException ( ) ;
                    reply. writeString (_result ) ;
                     return  true ;
                 }
             }
             return  super. onTransact (code, data, reply, flags ) ;
         }

         private  static  class  Proxy  implements 
            com. example. hello. MyWeatherAIDL {
             private android. os. IBinder mRemote ;

             Proxy (android. os. IBinder remote ) {
                mRemote  = remote ;
             }

            @Override
             public android. os. IBinder asBinder ( ) {
                 return mRemote ;
             }

             public java. lang. String getInterfaceDescriptor ( ) {
                 return DESCRIPTOR ;
             }

            @Override  public  int getWeather ( )  throws 
                android. os. RemoteException {
                android. os. Parcel _data  = android. os. Parcel. obtain ( ) ;
                android. os. Parcel _reply  = android. os. Parcel. obtain ( ) ;
                 int _result ;
                 try  {
                    _data. writeInterfaceToken (DESCRIPTOR ) ;
                    mRemote. transact ( Stub. TRANSACTION_getWeather, _data, _reply,  0 ) ;
                    _reply. readException ( ) ;
                    _result  = _reply. readInt ( ) ;
                 }
                 finally  {
                    _reply. recycle ( ) ;
                    _data. recycle ( ) ;
                 }
                 return _result ;
             }

            @Override  public  float getTemp ( int day )  throws 
                android. os. RemoteException {
                android. os. Parcel _data  = android. os. Parcel. obtain ( ) ;
                android. os. Parcel _reply  = android. os. Parcel. obtain ( ) ;
                 float _result ;
                 try  {
                    _data. writeInterfaceToken (DESCRIPTOR ) ;
                    _data. writeInt (day ) ;
                    mRemote. transact ( Stub. TRANSACTION_getTemp, _data, _reply,  0 ) ;
                    _reply. readException ( ) ;
                    _result  = _reply. readFloat ( ) ;
                 }
                 finally  {
                    _reply. recycle ( ) ;
                    _data. recycle ( ) ;
                 }
                 return _result ;
             }

            @Override  public java. lang. String getCity ( )  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_getCity, _data, _reply,  0 ) ;
                    _reply. readException ( ) ;
                    _result  = _reply. readString ( ) ;
                 }
                 finally  {
                    _reply. recycle ( ) ;
                    _data. recycle ( ) ;
                 }
                 return _result ;
             }
         }

         static  final  int TRANSACTION_getWeather  = 
             (android. os. IBinder. FIRST_CALL_TRANSACTION  +  0 ) ;
         static  final  int TRANSACTION_getTemp  = 
             (android. os. IBinder. FIRST_CALL_TRANSACTION  +  1 ) ;
         static  final  int TRANSACTION_getCity  = 
             (android. os. IBinder. FIRST_CALL_TRANSACTION  +  2 ) ;
     }

     public  int getWeather ( )  throws android. os. RemoteException ;
     public  float getTemp ( int day )  throws android. os. RemoteException ;
     public java. lang. String getCity ( )  throws android. os. RemoteException ;
}

我们粗略的看看该文件,首先它是和我们的aidl文件同名的一个java接口:MyWeather。其中包含了我们在aidl中定义的三个方法:

public  int getWeather ( )  throws android. os. RemoteException ;
public  float getTemp ( int day )  throws android. os. RemoteException ;
public java. lang. String getCity ( )  throws android. os. RemoteException ;

它们都可能会抛出android.os.RemoutException异常。然后Interface中有一个内嵌类叫:

public  static  abstract  class  Stub  extends android. os. Binder  implements com. example. hello. MyWeatherAIDL

很复杂对不对,首先它是个内嵌类因为static关键字,故而它和MyWeatherAIDL没太多联系实际上,它并不是一个内部类。然后呢,它继android.os.Binder类,这个类是干什么的?我们打开Binder的源码来搜寻点蛛丝马迹:

/**
31 * Base class for a remotable object, the core part of a lightweight
32 * remote procedure call mechanism defined by {@link IBinder}.
33 * This class is an implementation of IBinder that provides
34 * standard local implementation of such an object.
35 *
36 * <p>Most developers will not implement this class directly, instead using the
37 * <a href=”{@docRoot}guide/components/aidl.html”>aidl</a> tool to describe the desired
38 * interface, having it generate the appropriate Binder subclass. You can,
39 * however, derive directly from Binder to implement your own custom RPC
40 * protocol or simply instantiate a raw Binder object directly to use as a
41 * token that can be shared across processes.
42 *
43 * <p>This class is just a basic IPC primitive; it has no impact on an application’s
44 * lifecycle, and is valid only as long as the process that created it continues to run.
45 * To use this correctly, you must be doing so within the context of a top-level
46 * application component (a {@link android.app.Service}, {@link android.app.Activity},
47 * or {@link android.content.ContentProvider}) that lets the system know your process
48 * should remain running.</p>
49 *
50 * <p>You must keep in mind the situations in which your process
51 * could go away, and thus require that you later re-create a new Binder and re-attach
52 * it when the process starts again. For example, if you are using this within an
53 * {@link android.app.Activity}, your activity’s process may be killed any time the
54 * activity is not started; if the activity is later re-created you will need to
55 * create a new Binder and hand it back to the correct place again; you need to be
56 * aware that your process may be started for another reason (for example to receive
57 * a broadcast) that will not involve re-creating the activity and thus run its code
58 * to create a new Binder.</p>
59 *
60 * @see IBinder
61 */

文件一开始的说明注释如上说,大概有个数了,原来Binder类啊继承自IBinder类(= =),它是远程对象的鸡肋,不是基类。它是轻量级远程过程调用机制的核心部分。这个类可以给你提供一个远程对象的标准本地实现。我们地球人一般不手动写这个类,而是使用一个叫做aidl的工具来描述出你梦想中的接口,然后我们会帮你实现这个梦想,合理地。你可以,但是,直接去从Binder类实现你自己的自定义RPC协议或者简单地实现一个未加工过的Binder对象,把它当做一个token,来进行跨进程分享。这个Binder类是一个基础的IPC原生类,它对applicant的生命周期没有影响,它仅当创建它的进程还活着的时候才有效。所以为了正确地使用它,你必须在一个顶级app组件里明确地让系统知道,要让进程活下去。你必须时刻在脑子中记住的是,在哪些情况下你的进程会不辞而别,并且在这种情况下你需要在进程重启时重新创建一个新的Binder并且attach it,(好吧attach这个词是那么传神我无法用Chinese表达)。举个例子,如果你在Activity中使用Binder,你的activity进程在开始前随时都有可能被枪毙,如果activity重新启动了你得新建一个Binder,把它放回原处(正确处)。你需要知道你的进程可能会因为各种各样的原因被启动比如说接到广播,在这情况下是不需要重建activity的,因此你的重建Binder的代码可能不会被跑到,这是危险的哟。

OK,以上的翻译真是体力活,但是我们已经对Binder有了个粗略的了解,它就是远程对象的鸡肋嘛,是鸡肋,是基类。OK,那我们的Stub就是一个继承自远程对象鸡肋的抽象类,它同时实现了MyWeatherAIDL接口。也就是说如果继承自该类的话,那么我们必须要实现MyWeatherAIDL的三个方法(我们的梦想接口),同时,它就是个真正的远程对象了,它就是我们的Server(是Server不是Service)。那就让我们来实现它吧,创建一个WeatherService.java,来作为Server的保鲜容器,重写里面的onBind方法:

@Override
public IBinder onBind (Intent intent )  {
     // TODO Auto-generated method stub
     return  new MyWeatherAIDL. Stub ( )  {

        @Override
         public  int getWeather ( )  throws  RemoteException  {
             // TODO Auto-generated method stub
             return  100 ;
         }

        @Override
         public  float getTemp ( int day )  throws  RemoteException  {
             // TODO Auto-generated method stub
             return day ;
         }
    
        @Override
         public  String getCity ( )  throws  RemoteException  {
             // TODO Auto-generated method stub
             return  "wuxi" ;
         }
     } ;
}

OK,return的值是一个IBinder,IBinder是一个接口,故而我们返回的一定是一个实现了IBinder的对象,然后向上转型了对吧。返回值是new MyWeatherAIDL.Stub()对象,这是匿名内部类的写法比较简洁省事,(如果你对此不是很熟悉,请购买《Thinking in java》,购买地址:http://product.dangdang.com/1173405126.html,并且熟读其中的匿名内部类一章)。我们当然也可以用一种看得更加清楚明了的方式:

class MyAIDLImpl  extends MyWeatherAIDL. Stub {

    @Override
     public  int getWeather ( )  throws  RemoteException  {
         // TODO Auto-generated method stub
         return  100 ;
     }

    @Override
     public  float getTemp ( int day )  throws  RemoteException  {
         // TODO Auto-generated method stub
         return day ;
     }

    @Override
     public  String getCity ( )  throws  RemoteException  {
         // TODO Auto-generated method stub
         return  "wuxi" ;
     }
}

@Override
public IBinder onBind (Intent intent )  {
     // TODO Auto-generated method stub
    MyAIDLImpl myImpl = new MyAIDLImpl ( ) ;
     return myImpl ;
}

我们在Service中实现了一个MyAIDLImpl类,它就是Server,然后呢,里面具体实现了我们的梦想接口,也就是现实实现。然后我们在onBind方法里将其返回了,它经历了向上转型为MyAIDL.Stub再转型为Binder再转型为IBinder的过程。可能有人发现这样向上转型了有个p用,现实实现的方法无法调用了啊,是的别急,会给你向下转回来的。
接下来我们要用这个Server了吧,谁来用,Client来用,Client是谁,随便你,都可以。我们这里就选择最常规的Activity吧:

Intent serviceIntent = new Intent ( this,com. example. hello. WeatherService. class ) ;
bindService (serviceIntent, conn, BIND_AUTO_CREATE ) ;

在你想使用这个Server前先绑定它的容器Service,使用Context.bindService(Intent,ServiceConnection,int)方法,Intent是指明你的Service的“线索”,而ServiceConnection是我们马上要实现的一个接口,其中包含了一些关于bindService时会被调用的回调,而最后的int则是一些绑定选项,这个选项我还没有细究。以后出Service博客时我再细究。ok,这样调用bindService的过程中,Service的onBind方法被调用,IBinder被获取,并且传给了ServiceConnection实现的onServiceConnected回调方法中,来看一下ServiceConnection实现:

ServiceConnection conn = new ServiceConnection ( )  {

    @Override
     public  void onServiceDisconnected (ComponentName name )  {
         // TODO Auto-generated method stub
    
     }

    @Override
     public  void onServiceConnected (ComponentName name, IBinder service )  {
         // TODO Auto-generated method stub

     }
} ;

有两个回调,一看就知道意思,一个是Service断连后触发,一个是Service连接后触发。onServiceConnected的方法中传入了一个IBinder service,这个就是Server。Okay,只能说它是Server,但不一定是Server的真身,我们过会会了解到为什么这么说,暂且我们就把它当做是回来的MyAIDLImpl对象吧。
我们在MyActivity中创建一个MyWeatherAIDL对象的引用:MyWeatherAIDL mWeatherAnswer; 然后我们在onServiceConnected中这么做:

@Override
public  void onServiceConnected (ComponentName name, IBinder service )  {
     // TODO Auto-generated method stub
    mWeatherAnswer =MyWeatherAIDL. Stub. asInterface (service ) ;
}

这样,通过MyWeatherAIDL.Stub的asInterface方法将service转换成了MyWeatherAIDL对象,(我说过会给它转回来的),ok,这样我们就可以安心地使用了我们的梦想接口的方法了,getXX,getXX,然后Server就回不断地回答你的问题。

好了,AIDL就是这么简单。
但是到底发生了什么?
asInterface只是简单地向下转型再向上转型么?总觉得AIDL这么方便我好像什么都还没做有点良心不安诶。。。而且自动生成的梦想接口java文件那么多的东西是干嘛的,不看看有点小紧张诶。既然我们这么想,那就一窥这神奇的世界吧。

依旧是看那个自动生成的MyWeatherAIDL.java文件。不用太费心思翻上去看了,我会把它一段段粘贴下来的,贴心的博主万岁。
首先我们直奔的一定是Stub.asInterface方法:

/**
* Cast an IBinder object into an com.example.hello.MyWeatherAIDL interface,
* generating a proxy if needed.
*/


public  static com. example. hello. MyWeatherAIDL asInterface (android. os. IBinder 
obj ) {
     if  ( (obj == null ) )  {
         return  null ;
     }
    android. os. IInterface iin  = obj. queryLocalInterface (DESCRIPTOR ) ;
     if  ( ( (iin != null ) &amp ;&amp ; (iin  instanceof 
            com. example. hello. MyWeatherAIDL ) ) )  {
         return  ( (com. example. hello. MyWeatherAIDL )iin ) ;
     }
     return  new com. example. hello. MyWeatherAIDL. Stub. Proxy (obj ) ;
}

方法的说明注释的意思是,该方法把一个IBinder对象转型为一个MyWeatherAIDL对象,必要的时候生成一个Proxy,即代理。
代码中看,首先调用了传入的IBinder:obj的IInterface queryLocalInterface(String)。然后根据其返回的IInterface iin是否是MyWeatherAIDL接口的实例来决定返回向下转型成MyWeatherAIDL的对象,还是一个包装进new com.example.hello.MyWeatherAIDL.Stub.Proxy(obj);的对象。我看到这里的时候非常疑惑,首先什么是queryLocalInterface,传入的String是
private static final java.lang.String DESCRIPTOR = “com.example.hello.MyWeatherAIDL”;
这是个编译期常量。我先直接说明一下,首先,queryLocalInterface方法呢,是根据提供的Interface名,来查看一下你这个obj(IBinder)是否是一个本地进程中的接口对象实例,如果是呢,当然可以直接强制类型转换,返回引用。但是也可能不是本地进程中的借口的对象实例,比如说我在其他进程中定义的MyWeatherAIDL实例(Server),它在onBind返回时,返回的其实,这里是关键,它其实是一个BinderProxy。顾名思义,它没给你一个Binder真身的引用,它只是给了你一个代理。而这个BinderProxy是何方神圣?它被定义于
/frameworks/base/core/java/android/os/Binder.java
文件中,也就是说和Binder在同一个文件里,但它并不是Binder的内部类哦。

final  class BinderProxy  implements IBinder

它也实现了IBinder接口,同时它的queryLocalInterface方法:

public IInterface queryLocalInterface ( String descriptor )  {
     return  null ;
}

该方法是不分情况直接返回null的,也就是说,在Stub的asInterface方法中,只要obj是一个BinderProxy,立刻创建MyWeatherAIDL.Stub.Proxy对象出来封装这个BinderProxy。

好了,我们可以回头梳理一下了,我们在MyWeatherService的onBind方法中创建了我们的Server:MyAIDLImpl的对象,然后将它作为IBinder返回了,Client的bindService方法中获取了一个IBinder传递给了OnServiceConnected方法,但是这个IBinder不一定是Server的引用:
(1)如果MyWeatherService是和Client同进程的话,那么返回的是MyAIDLImpl对象的引用。
(2)如果MyWeatherService在另一个进程中运行着的话,那么此时IBinder是一个BinderProxy。

应该说的很清楚了。那么MyWeatherAIDL.Stub.asInterface方法也很明确表示,(1)的情况我们直接使用的就是这个Server的引用,这种情况下我们是在直接调用方法okay?这不是IPC,仅仅是直接调用方法。(2)的情况才是真正的IPC,我们获取的只是一个代理,它的大哥在另一个process中逍遥呢,所以我们没法直接联系Server,只能靠它的代理来沟通问题。
我们下面就可以专注于代理的情况了。我们试着让Client调用一下,其实我觉得我们在Activity中创建的mWeatherAnswer是一个比Activity更加精确的Client。mWeatherAnswer.getTemp(1); 这样调用发生了什么?首先我们已经知道,mWeatherAnswer是一个MyWeatherAIDL.Stub.Proxy。我们聚焦一下:

private  static  class  Proxy  implements com. example. hello. MyWeatherAIDL
{
     private android. os. IBinder mRemote ;
     Proxy (android. os. IBinder remote ) {
        mRemote  = remote ;
     }

    @Override
     public android. os. IBinder asBinder ( ) {
         return mRemote ;
     }

     public java. lang. String getInterfaceDescriptor ( ) {
         return DESCRIPTOR ;
     }

    @Override  public  int getWeather ( )  throws android. os. RemoteException {
        android. os. Parcel _data  = android. os. Parcel. obtain ( ) ;
        android. os. Parcel _reply  = android. os. Parcel. obtain ( ) ;
         int _result ;
         try  {
            _data. writeInterfaceToken (DESCRIPTOR ) ;
            mRemote. transact ( Stub. TRANSACTION_getWeather, _data, _reply,  0 ) ;
            _reply. readException ( ) ;
            _result  = _reply. readInt ( ) ;
         }
         finally  {
            _reply. recycle ( ) ;
            _data. recycle ( ) ;
         }
         return _result ;
     }

    @Override  public  float getTemp ( int day )  throws 
        android. os. RemoteException {
        android. os. Parcel _data  = android. os. Parcel. obtain ( ) ;
        android. os. Parcel _reply  = android. os. Parcel. obtain ( ) ;
         float _result ;
         try  {
            _data. writeInterfaceToken (DESCRIPTOR ) ;
            _data. writeInt (day ) ;
            mRemote. transact ( Stub. TRANSACTION_getTemp, _data, _reply,  0 ) ;
            _reply. readException ( ) ;
            _result  = _reply. readFloat ( ) ;
         }
         finally  {
            _reply. recycle ( ) ;
            _data. recycle ( ) ;
         }
         return _result ;
     }

    @Override  public java. lang. String getCity ( )  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_getCity, _data, _reply,  0 ) ;
            _reply. readException ( ) ;
            _result  = _reply. readString ( ) ;
         }
         finally  {
            _reply. recycle ( ) ;
            _data. recycle ( ) ;
         }
         return _result ;
     }
}

它是Stub的内嵌类,它也实现了MyWeatherAIDL接口,所以它也需要实现梦想接口中的方法,它已经自动为我们生成好了这三个getXX形式的方法。我们就拿float getTemp(int day)来举例吧。首先它先通过android.os.Parcel.obtain();获取了两个Parcel对象:_data,_reply。这个_data的作用是用来将Client调用getTemp(int day)时传入的参数列表给封装起来,而_reply则是为Server处理调用后的返回结果而准备的。_data先写入TOKEN,然后通过Parcel类的writeXX类型方法将参数列表中的参数依次按照对应类型写入,此处只有一个int参数,所以是writeInt(day)。然后调用mRemote.transact(Stub.TRANSACTION_getTemp, _data, _reply, 0);mRemote就是被Proxy封装起来的那个obj哦,也就是BinderProxy,别忘记了。让代理调用transact方法,其中,Stub.TRANSACTION_getTemp定义在了Stub类中,是编译期常量,每个梦想接口的方法都有一个对应的int类型的方法标识:

static  final  int TRANSACTION_getWeather  =  (android. os. IBinder. FIRST_CALL_TRANSACTION  +  0 ) ;
static  final  int TRANSACTION_getTemp  =  (android. os. IBinder. FIRST_CALL_TRANSACTION  +  1 ) ;
static  final  int TRANSACTION_getCity  =  (android. os. IBinder. FIRST_CALL_TRANSACTION  +  2 ) ;

这个方法标识参数是要告诉Server,我是请求调用这个那个方法的。然后transact方法再传入_data用于告诉Server,参数在这个Parcel里啊。然后传入_reply参数,高速Server,返回的结果放到这个Parcel里即可。最后一个0,这个嘛,暂时我也还没研究得这么细致,以后再说吧。

OK,transact方法这么一调用会发生什么呢?IPC。
我们看一下IBinder类对transact的说明,注意,BinderProxy是一个IBinder,我们要看的是IBinder对transact方法的说明:
没有sorry,该方法仅仅是个接口,具体实现具体分析,所以它仅仅说了几个参数干嘛的。那就去BinderProxy去看看有什么线索,很遗憾,它没有说明注释,但是它的方法很简短:

public  boolean transact ( int code, Parcel data, Parcel reply,  int flags )  throws     RemoteException  {
    Binder. checkParcel ( this, code, data,  "Unreasonably large binder buffer" ) ;
     return transactNative (code, data, reply, flags ) ;
}

先让Binder处理了一下传入的_data这个Parcel,然后transactNative方法,一溜烟把函数代号,两个parcel传到native方法调用去了。我们知道,这些数据都去了底下C++的代码层里了。就好像打了个地洞,直奔地核了一样。我暂时不想讨论到了C++层会发生些什么。请重新看一下我们这篇博客的标题,请大声读一遍,AIDL使用方法浅析,方法浅析,浅析,析。。。。。。请不要感到难过,我们也许还能找到些蛛丝马迹,请看一下IBinder一开始的一段说明注释:

……
*The key IBinder API is {@link #transact transact()} matched by
30 * {@link Binder#onTransact Binder.onTransact()}. These
31 * methods allow you to send a call to an IBinder object and receive a
32 * call coming in to a Binder object, respectively. This transaction API
33 * is synchronous, such that a call to {@link #transact transact()} does not
34 * return until the target has returned from
35 * {@link Binder#onTransact Binder.onTransact()}; this is the
36 * expected behavior when calling an object that exists in the local
37 * process, and the underlying inter-process communication (IPC) mechanism
38 * ensures that these same semantics apply when going across processes.
……

IBinder的这段说明很长很精彩,我只截取了一段最关键的关于transact的说法。IBinder的transact方法和Binder对象的onTransact配套。这些方法允许你对IBinder对象发送一个调用,然后从一个Binder对象中获取一个调用,分别地。(这个分别地告诉我们Binder对象和IBinder对象不是同一个对象,其实也就是暗指IBinder就是BinderProxy就是Client,而Binder就是Server)这个transaction API是同步的,transact调用完了以后,必须等到Binder对象的onTransact方法返回一个才能继续往下走,否则就阻塞在那里等待。这是我们在本地进程中调用一个对象来使用IPC机制,所期望的一种行为。并且,这种手段确保了我们在用IPC机制进行跨进程通信时,使用和本地调用一样的语法。(就好像直接调用了一个对象的一个方法一样,而完全感觉不到跨进程调用的存在)

我们已经知道了我们想知道的东西。提取一下上面的信息就是,我们BinderProxy(Client)的transact调用,会引发Binder(Server)的onTransact回调,然后Client等待onTransact做完,才能继续往下走,onTransact做完时,函数返回结果已经存在了_reply里,我们接着往下看一下:

_reply. readException ( ) ;
_result  = _reply. readString ( ) ;

这就是读一下结果,是不是有异常啊,返回的结果存在_result变量中,继续最后的工作:

finally  {
    _reply. recycle ( ) ;
    _data. recycle ( ) ;
}
return _result ;

finally里进行了Parcel的释放工作,最后将函数结果返回。此时Client这里发生的一切我们就已经很明确了。那Server那边onTransact方法做了什么呢?我们看一下另一个进程中的Server,它是一个MyWeatherAIDL.Stub子类:

@Override  public  boolean onTransact ( int code, android. os. Parcel data, 
    android. os. Parcel reply,  int flags )  throws android. os. RemoteException {
     switch  (code ) {
         case INTERFACE_TRANSACTION :
         {
            reply. writeString (DESCRIPTOR ) ;
             return  true ;
         }
         case TRANSACTION_getWeather :
         {
            data. enforceInterface (DESCRIPTOR ) ;
             int _result  =  this. getWeather ( ) ;
            reply. writeNoException ( ) ;
            reply. writeInt (_result ) ;
             return  true ;
         }
         case TRANSACTION_getTemp :
         {
            data. enforceInterface (DESCRIPTOR ) ;
             int _arg0 ;
            _arg0  = data. readInt ( ) ;
             float _result  =  this. getTemp (_arg0 ) ;
            reply. writeNoException ( ) ;
            reply. writeFloat (_result ) ;
             return  true ;
         }
         case TRANSACTION_getCity :
         {
            data. enforceInterface (DESCRIPTOR ) ;
            java. lang. String _result  =  this. getCity ( ) ;
            reply. writeNoException ( ) ;
            reply. writeString (_result ) ;
             return  true ;
         }
     }
     return  super. onTransact (code, data, reply, flags ) ;
}

首先,它先要看看,你Client企图调用我的什么那个方法。然后进行入对应的case。我们就进入TRANSACTION_getTemp条目。读取了DESCTIPTOR,再挨个将每个参数列表中的参数读取出来,然后轻而易举地调用float this.getTemp(int)方法获取一个返回值,这个方法的具体实现就是我们在MyWeatherService中实现的那个实现,就是这样。然后呢,将返回值写到_reply里,然后writeNoException(),return true,表示没有抛出异常,正常执行了。
一个IPC过程就这样顺利轻松地做完了。
好了现在你一定已经明白了AIDL是什么了,也知道它只是一个帮助我们快速简单地进行IPC通信的手段,对于开发者来说,一大段固定的C/S协议代码无需我们一行行手动书写。但是,我们必须得知道这其中是有故事的。
但是千万别认为AIDL很简单,或者准确地说,别认为Binder很简单,它里面非常复杂,到了C++那一层有很多很多的学问,具体看看罗升阳前辈的非常详尽的N篇关于AIDL,IPC,Binder原理的系列文章吧:,非常有难度。我这里这篇博文仅仅起到让完全不懂AIDL的应用开发者能够理解AIDL为何物,如何使用,的作用而已。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值