Service与Android系统实现(1)-- 应用程序里的Service (四)

AIDL的内部实现

aidl工具的工作原理也很简单, aidl工具的源代码在frameworks/base/tools/aidl里,如果对通过bison来实现编译器感兴趣也可以参考其实现。

而AIDL工具所完成的工作,是将aidl文件转义成一个通用的Java文件,我们实现的内容,便是拓展自这一Java文件里的定义。aidl工具生成的结果,一般与aapt工具生成的结果放在同一目录,在应用程序环境里,aidl生成的结果是一个在gen/包名/目录里与aidl文件前缀名相同的Java文件,我们的例子里会是gen/org/lianlab/services/ITaskService.java,我们可以看一下这个生成文件的内容:

[java] view plain copy
  1. package org.lianlab.services;  
  2. public interface ITaskService extends android.os.IInterface { 1  
  3.     public static abstract class Stub extendsandroid.os.Binderimplements  
  4.            org.lianlab.services.ITaskService { 2  
  5.        private static final java.lang.String DESCRIPTOR = "org.lianlab.services.ITaskService";   3  
  6.    
  7.        public Stub() {  
  8.            this.attachInterface(this,DESCRIPTOR);   4  
  9.        }  
  10.    
  11.        public static org.lianlab.services.ITaskService asInterface(android.os.IBinderobj) {  5  
  12.            if ((obj == null)) {  
  13.                 return null;  
  14.            }  
  15.            android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR);  
  16.            if (((iin != null) && (iin instanceof org.lianlab.services.ITaskService))) {  
  17.                 return((org.lianlab.services.ITaskService) iin);  
  18.            }  
  19.            return new org.lianlab.services.ITaskService.Stub.Proxy(obj);  6  
  20.        }  
  21.    
  22.        public android.os.IBinder asBinder() {  
  23.            return this;  
  24.        }  
  25.    
  26.        @Override  
  27.        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply,  
  28.                 int flags)throwsandroid.os.RemoteException {  7  
  29.            switch (code) {  
  30.                 case INTERFACE_TRANSACTION: {  8  
  31.                     reply.writeString(DESCRIPTOR);  
  32.                     return true;  
  33.                 }  
  34.                 case TRANSACTION_getPid: {    9  
  35.                     data.enforceInterface(DESCRIPTOR);  
  36.                     int _result =this.getPid();  
  37.                     reply.writeNoException();  
  38.                     reply.writeInt(_result);  
  39.                     return true;  
  40.                 }  
  41.            }  
  42.            return super.onTransact(code, data, reply, flags);   10  
  43.        }  
  44.    
  45.        private static class Proxy implements org.lianlab.services.ITaskService { 11  
  46.            private android.os.IBindermRemote;  12  
  47.    
  48.            Proxy(android.os.IBinder remote) {  
  49.                mRemote = remote;  
  50.            }  
  51.    
  52.            public android.os.IBinder asBinder() {  
  53.                 return mRemote;  
  54.            }  
  55.    
  56.            public java.lang.StringgetInterfaceDescriptor() {  
  57.                 return DESCRIPTOR;  
  58.            }  
  59.    
  60.            public int getPid() throws android.os.RemoteException { 13  
  61.                 android.os.Parcel _data =android.os.Parcel.obtain();  
  62.                 android.os.Parcel _reply =android.os.Parcel.obtain();  
  63.                 int _result;  
  64.                 try {  
  65.                     _data.writeInterfaceToken(DESCRIPTOR);  
  66.                     mRemote.transact(Stub.TRANSACTION_getPid,_data, _reply, 0);  
  67.                     _reply.readException();  
  68.                     _result = _reply.readInt();  
  69.                 } finally {  
  70.                     _reply.recycle();  
  71.                     _data.recycle();  
  72.                 }  
  73.                 return _result;  
  74.            }  
  75.        }  
  76.    
  77.        static final int TRANSACTION_getPid = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);    14  
  78.     }  
  79.    
  80.     public int getPid() throwsandroid.os.RemoteException;   
  81. }  

       上述生成的ITaskService.java文件,在类图上大致由如下的关系来构成:

Service与Android系统实现(1)-- 应用程序里的Service (四)_第1张图片

IInterface接口类在Binder框架里是两边访问的基础,只通过asBinder访问一个IBinder对象。在Proxy模式里核心接口类Interface部分的功能,实际上则由拓展IInterface的自定义的ITaskService来完成,通过同一接口,客户端会得到Proxy对象,而Service会得到Stub对象。在Android环境里,Proxy只是Stub类的一部分,这是Proxy在实现上的一种变体,从而强调Stub作为实现上的重要性,Proxy需要依赖于其对应的Stub接口的存在。我们可以详细分析一下,这些自动生成的代码的含义:

  1. 定义一个ITaskService接口类,继承自同样是接口类的IInterface。IInterface接口类的作用很简单,就是能够通过asBinder()方法返回一个IBinder接口对象。IBinder本质上也是接口类,不过比IInterface要复杂,这一接口用来细分出一些Binder通信上的接口,从而基于同一接口,在发送端实现发送代码transact(),在接收端实现接收代码onTransact()。像我们前面看到的所有Binder对象,其实都是继承自这一接口,于是使用Binder对象天生是跨进程对象,所以有继承自Binder的类,被创建后都会是具备跨进程能力的远程对象。从这里的实现,我们也可以看到,IBinder与Binder 的接口与实现抽离,可以使用我们可以灵活地使用统一的IBinder接口访问到不同进程空间里的Binder对象。ITaskService接口类,实际上很简单,就是定义一个抽象类Stub,以及aidl里定义的接口方法。
  2. 定义一个抽象类Stub,同时继承Binder类,并实现ITaskService接口。继承Binder,使Stub对象将成为跨进程的远程对象,而实现ITaskService接口,则使Binder IPC通信里发生的ITaskService相关的操作,都将转发到Stub对象的onTransact()方法里处理。作为抽象类的Stub,于是它不可能单独被实例化,必须通过实现抽象类里缺失的方法来得到一个实质的类,因为我们的ITaskService接口类只需要getPid()实现,于是我们的Stub所缺失的也只有这一方法。
  3. Stub里会包含一个静态、final类型的字符串DESCRIPTOR,这一字符串用于Binder通信时的标识。DESCRIPTOR在aidl翻译时会使用aidl接口类的名字,所以这一字符串是不会重复的。
  4. 在Stub对象的初始化方法里,会调用attachInterface()将DESCRIPTOR注册到Binder对象里,于是当一个完整实现的Stub对象被创建时,以它的接口类名为标识的信息就会注册到Binder通信里,从而Binder IPC里以 DESCRIPTOR为目标的消息就会传到这一Stub实现里。
  5. asInterface()是提供给客户端来调用的方法。在基于aidl的编程里,客户端通过onServiceConnected()会取回一个IBinder引用,而我们通过Stub对象的asInterface()方法,就可以取回一个客户端可用的Proxy实例。这是Android的技巧所在,通过onServiceConnected()返回的,只是一个IBinder引用,在进程空间不会存在具体的对象,因为通过一个接口类ITaskService是无法得到具体的对象的,于是通过这个asInterface()的所谓类型转换,实质却是通过这次调用创建了一个Proxy对象。于是客户端有了Proxy对象,向Service端的Stub端对象发送命令,这样RPC交互便建立起来了。
  6. 在建立起与Stub对象通信的过程前,本地IInterface为空引用,于是创建一个Stub.Proxy对象。在后面的Stub.Proxy实现部分,我们可以看到在Stub.Proxy对象被创建后,调用ITaskService的方法,实际上会通过不同的接口方法实现,发送Binder命令到远端。
  7. 对于Stub端代码的实现,我们前面也分析过,就是通过把IPC消息取出来解析,找到需要执行哪个方法来响应具体的调用请求。在Android环境里,这些操作统一由onTransact()方法来完成。在Java里对于异常的捕捉与处理很重要,而我们所有的远程方法调用,实际都会是在onTransact()作用域里来完成,于是我们在这里抛出RemoteException,远程调用上任何的异常信息都将在这时会集中到一起,通过Binder抛到客户端处理。
  8. INTERFACE_TRANSACTION,值为1,这是Binder跨进程交互的最基本通信接口,用于查询这一接口是否存活。这一TRANSACTION在处理上就比较简单,只是将自己的DESCRIPTOR写入Binder返回给客户端。
  9. TRANSACTION_getPid,对于aidl里定义的每个方法,最终都会将当前Binder通信的命令+1,从而可以拓展出新的命令请求,于是这个值在客户端环境和Service端环境都是统一的。在通过Binder接收这样的Binder命令之后,就会调用具体的Stub里实现的方法,比如我们实现的getPid(),然后再将返回值通过Binder传回给客户端。
  10. 这是IoC设计模式的又一次应用。在Android源代码里,我们经常看到这样成功实施的设计模式反复被使用,简直让人怀疑这整套系统都是由一个人设计实现。在Stub类里,实际上继承了Binder类然后又覆盖掉了onTransact()方法,而在覆盖掉的onTransact()方法里又回调到父类Binder的onTransact()方法,实际上就拓展了Binder的执行能力,但又保持了原有的接口方法。
  11. 定义客户端调用所需要的Proxy类。Proxy与Stub一样,都继承自ITaskService,于是也会跟Stub通过ITaskService接口类的定义来共享接口方法。
  12. 在这里,我们就可以看到接口与实现分享的好处了,我们的代码通过IBinder接口来访问某一对象,于是在Service实现里得到的Stub,而在客户端得到的是Stub.Proxy对象。这样的特点在继承于Binder的远程对象里也是如此,Binder对象同时存在客户端的请求代码与Service端的响应代码,而在客户端与Service端都使用IBinder接口来访问Binder时,就可以得到不同的实现。
  13. 这是我们Proxy部分的代码实现,比如我们例子里定义的是getPid()的接口方法,在这里就会有一个getPid的Proxy接口方法实现。在这一方法里,会通过Binder通信,将TRANSACTION_getPid的命令发送到标识为DESCRIPTOR的响应请求的部分。对应于我们前面描述的onTransact()实现,我们就会知道,后续动作就是触发远程的getPid()被执行到。消息发送会是阻塞操作,当代码得以继续时,远程代码肯定已经执行完成,这时就会通过Binder将远程发过来的返回结果读出来,返回给调用getPid()的部分。当然,远程的onTransact()实际上还有可能通过Binder将RemoteException抛出来,于是我们这里也将读取这些Exception,再转发给上层异常捕捉代码。
  14. 所有的Binder所能收发的命令,都是按INTERFACE_TRANSACTION + n的方法加入系统里的,这样拓展起来很灵活。由于都是基于同一ITaskService.java来提供这些命令定义,于是客户端与Service端是共享这些命令定义的。
所有的上述的代码,就是只为了实现一个接口方法而用,要是有更多的接口方法的定义,则这些自动生成的代码将会很长。代码复杂,又是需要跨进程交互的处理,在编程上很容易出错,而aidl工具的使用,大大减小了我们编程的工作量,也减小了出错的概率。

总而言之,这些代码,就将客户端与Service端的代码,分别通过Stub与Stub.Proxy两个类来创建,通过这两个对象的引入,无论是调用RPC,还是提供RPC实现,都变得只是拓展一下原有的基本实现即可。这时就基本上达到我们前面描述过的基于Binder进行RPC通信的需求:

Service与Android系统实现(1)-- 应用程序里的Service (四)_第2张图片 

而在远程交互的实现上,我们基于AIDL的Remote Service,就会以如下的形式进行交互:

Service与Android系统实现(1)-- 应用程序里的Service (四)_第3张图片

在编译阶段,idl文件里定义的接口会通过aidl工具翻译得到一个ITaskService.java文件,保存到gen供应用程序引用。这一ITaskService.java文件里会具体实现发送请求的Proxy接口类与提供实现并将执行结果返回的Stub接口类。在发生aidl调用时:

  1. 客户端调用bindService(),以某个Intent作为参数。这一Intent里会通过Action将请求发送到Service实现,于是触发Service实现的onBind()回调。
  2. Service里实现onBind()会返回一个IBinder引用,通过触发客户端的onServiceConnected()回调方法,传递给客户端。实际上,在建立Binder通信之前的这些跨进程通信都是由ActivityManager来完成的,并不是直接跟Binder打交道,而是由Intent来触发。
  3. 取回IBinder引用之后,通过ITaskService.Stub.asInterface()方法,这一Binder会被转换成Proxy对象。当然,我们从底层已经看到,这个所谓的转换,实质上是创建了一个ITaskService.Stub.Proxy对象。
  4. 当我们需要从客户端进程访问到服务端进程时,实际上,都是通过已经取得的Stub.Proxy对象里的getPid()方法,将一个执行的请求通过Binder发送给Service里实现的Stub对象。
  5. Stub对象通过自己的onTransact()方法,读取发送到自己进程的Binder命令,根据不同Binder命令执行不同的远程方法,然后将执行完的结果通过Binder返回给客户端。

当然,此时我们得到了基本的跨进程调用方法的能力,这跟我们普通的编程模型一致了,像我们的一般的编程,都是从一个main()函数开始,调用不同的代码逻辑,最终得到一个大的复杂的可执行程序。但这并非全部,在现实的编程环境里,我们还需要实现一些回调式的编程,主要用于一些直接调用不太合适的情境。比如我们需要在后台做某些比较耗时的操作,像下载、登录等,如果直接调用再取返回值,此时,我们得到的结果就很悲惨,会需要等待函数执行完成之后才能继续执行,需要等待很长时间。在Android的单线程编程模型里,这种情况会更严重,我们Activity运行在主线程里,如果通过aidl调用到一个耗时操作,就会阻塞到主线程的执行,从而产生ANR错误。

这种方式实现的,只是单向调用,虽然我们也可以在发起调用的部分取得返回值,但只是一种单向的函数式调用。如果我们需要实现双向调用怎么做呢?我们不光会调用到远程代码里,也可以提供一种回调接口,让被调用方回调回来,就像是C语言里的回调函数,这样,远程调用的世界就完整了,见后续内容。





你可能感兴趣的:(Service与Android系统实现(1)-- 应用程序里的Service (四))