分享

Android AIDL之浅尝

 风雪夜归人_95 2014-08-26
         AIDL是一个接口描述文件,用于实现Android平台上的RPC,aapt在编译的时候会自动根据规则生成用于IPC的接口和对象,而作为使用者只需要:1. 在服务端Service实现接口;2. 在客户端bindService,onServiceConnected时获取接口对象。这里的接口都是AIDL中描述的接口,其他的细节则在由AIDL 生成的同名源码文件中。
       AIDL的角色是实现Android平台上面的RPC(Remote Procedure Call)也即远程例程调用。RPC是IPC中的一种,但是它是以调用在本地或者另一个进程,甚至是另一个主机上的方法的机制。RPC的目的就是可以让程 序不用担心方法具体是在哪个进程里面或者哪以机器上面,就像正常的本地方法那样去调用即可,RPC机制会处理所有的具体细节。RPC一般用 IDL(Interface Definition Language)来描述,实现则要看具体的平台和语言。
        可以通过分析gen文件夹下生成的AIDL文件同名的源码文件,来了解AIDL的本质。首先,最外面是与AIDL同名的接口类。这个类下面声明(仅写出了各个函数的原型,无实现,故这里称为声明)了AIDL中定义的方法。然后里面就是一个Stub类。客户端使用时用到Stub类下的asInterface方法,这个方法会返回一个实现了PrinterInterface接口的对象。而服务器端需要实现这个Stub。Stub中还定义了一个类Proxy。这个Proxy类就是用来远程调用的。
 android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.amaker.ch07.app.IPerson))) {
return ((com.amaker.ch07.app.IPerson)iin);
}
return new com.amaker.ch07.app.IPerson.Stub.Proxy(obj);
}
这段代码来自上面提到的asInterface方法。其逻辑是:查找本地是否有接口的实现,如果有则返回Service.onbind()所返回的对象,若没有则创建一个Proxy类。实际上,这里是在判断本次通讯是否在同一个进程中完成。若是同一个进程中直接返回Service.onbind()的对象;当不在同一个进程中就要使用Binder对象的IPC相关的方法和机制。客户端需要实现 Binder.transact()方法来执行远程的一个方法,这是给客户端来使用;而服务端则需要实现Binder.onTransact()来响应客 户端所请求的方法。对于上层使用者来说,用transact()把函数的信息(参数,标识和开关)发送出去,剩下的就是Binder的工作了,内部还有大 量的细节,但是最终会调用到服务端Binder的onTransact()方法,这里识别出函数的标识,然后调用具体的实现,再传回返回值,这样一个 IPC的函数调用就完成了。

        AIDL本质作用就是对BInder的两个方法Binder.transact()和Binder.onTransact()进行封装,以供Client端和Server端进行使用。因为实现transact()和onTransact()方法的方式基本上是相同的,所以就 可以用模板来生成具体的代码。理论上讲只需要为Client端生成transact()相关代码,为服务端生成onTransact()代码即可,但因为 工具无法准确的确定某一个应用到底是Client端还是Server端,所以它就生成所有的代码,放在一个文件中。这就是你看到的自动生成的文件也正是基于这个原因,有人提出不使用AIDL,而自己根据AIDL进行重写,避免代码的冗长
     
还需要注意的一点是Client端的Proxy是组合Binder对象,调用其 transact()方法;而服务端必须继承Binder对象,覆写onTransact()方法。因为Client是主动发起IPC函数 Call,所以它可以直接调用Binder的方法来进行IPC。而Server是被动的,是要接收进来的IPC call,但Service自己无法得知啥时候Call会来,因此必须实现回调(onTransact())给Binder,以让Binder在有IPC Call进来的时候告诉Service。

实例:通过Service来远程调用一个接口子类的函数方法
功能描述:在MainActivity中通过绑定MyService服务类,来远程调用MyPlayer(实现了IPlayer接口)的方法过 程。需要定义一个IPlayer.aidl文件,ADT工具会自动生成一个IPlayer接口类,然后再由MyPlayer继承IPlayer接口类中的 静态内部抽象类,实现接口方法,进而供其它应用程序远程调用。(在本例中为了方便,MainActivity与MyService类同处一个应用程序中, 实现运用时,可以不在同一个应用程序中,只要有权限访问MyService服务,就能得到IPlayer接口,进而执行该接口实例方法)


下面详细分析一下AIDL如何具体调用远程方法的过程:
1)还是从gen文件夹下自动生成的那个与AIDL同名的文件开始。那个接口继承了android.os.IInterface,是所有由AIDL文件生成的基类。接口中有一个内部类Stub,它继承自Binder并实现了这个生成的java接口,但没有实现我们定义的接口方法(由远程服务类来实现)。
2)继续看这个接口类。在stub中实现了一个很重要的方法asInterface(android.os.IBinder obj)。该方法中会去查询是否有一个ISecondary的实例,这其实是去查询是不是在同一个应用里去调用它,那我们就不用实行远程调用,直接本地调 用就可以了。如果不是本地接口,这时候会返回一个Proxy对象。Proxy类是Stub的一个内部类,也同样实现了java接口。但是它却 已经实现了这些接口方法。这就意味着如果要进行远程调用,必须获取一个Proxy类的实例,自然是通过stub类的asInterface方法获得。
3)在定义服务时的onServiceConnected方法中获得了远程服务的实例,而ActivityManagerService在bindService时,会调用ActivityThread的方法,并会传递一个Binder引用, 而ActivityThread会回调ServiceConnection中的OnServiceConnected方法,并将这个Binder对象传 入,也就是anInterface方法中的这个service。这样整个流程走完就获得了远程实例。

 
 

上图来自http://www.cnblogs.com/noTice520/archive/2012/11/01/2750209.html   ,综合演示了接口的实现是否来自本进程时的函数调用关系。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多