解读Android之Service(3)AIDL

本文翻译自android官方文档

Android Interface Definition Language(AIDL)能够让我们定义自己的编程接口,该接口可以使得客户端和service之间进行跨进程通信(interprocess communication,IPC)。通常,在android中无法直接跨进程通信。因此,需要把传递的对象分解成系统可以识别的原始状态(数据),并将它们跨进程序列化marshalling。由于marshalling过程繁琐,因此android通过AIDL处理。

注意:只有当我们允许不同应用程序的客户端获取service来进行IPC,并且在service中需要处理多线程时,AIDL才是必须的。绝大多数应用程序都不应该用AIDL来创建Bound Service,因为这可能需要多线程处理能力并且会让代码变得更为复杂。因此,AIDL对绝大多数应用程序都不适用。如果只是在应用程序内部使用,并且不需要跨进程,我们可以通过继承Binder类直接进行交互,这种是最常见的方式。若跨进程IPC且不需要处理多线程问题可以通过使用Messenger方法,因为Messenger把所有请求都放在一个线程中,因此不必担心线程安全问题。

(下面的理解上还有些问题)在开始设计AIDL接口之前,需要注意的是调用AIDL接口是直接的调用方法,我们不应该假设调用方法发生在子线程。从本地进程和远程进程中的线程调用是不同的:

  • 在本地进程中调用。若在主线程中调用,则AIDL接口会在主线程中执行。若是另外的子线程,则该线程执行service中的代码。因此 若只有本地线程获取service,我们能够完全控制。这种情况不应该使用AIDL,而是继承Binder实现。
  • 若是远程进程调用,则AIDL的实现必须要保证完全地线程安全。这是因为若是支持远程调用的话可能需要同时处理多个调用(并发处理)。
  • 单向改变远程调用行为。当使用这种方式时,远程调用不会阻塞;它简单的发送数据,并立刻返回。最终,接口实现接收向常规的通过Binder线程池调用一样处理远程调用。若单向用在本地调用,则不会影响并且调用仍是异步的。

上述内容理解有的问题,须再查阅资料并验证,同时请各位不吝赐教。

定义AIDL接口

AIDL接口必须定义在.aidl文件中(命名满足java语法),并同时保存在service所在的应用程序和其它绑定该service的应用程序(需要通过AIDL进行IPC的service)中,保存位置为源代码中src/目录下。

当我们新建一个.aidl文件时,android SDK工具就会根据该文件自动生成一个IBinder接口,并且保存在gen/目录下。service必须实现IBinder接口,客户端才能绑定service并调用方法获得该对象进行IPC。

上面的目录是在eclipse中的,在android studio中则在: 
aidl文件位置

为了能够创建使用AIDL的service,必须要实现以下步骤:

  1. 创建.aidl文件 
    该文件定义了带有方法声明的编程接口
  2. 实现接口 
    android SDK工具使用java生成一个接口,依据是根据.aidl文件。这个接口中有一个内部抽象类Stub,该类继承了Binder类,并且实现了AIDL接口中的方法,我们必须继承Stub类和实现其方法。
  3. 将接口暴露给客户端 
    实现Service类,覆盖onBind()方法,并且返回Stub的实现。

注意:在我们第一次发布之后的改变AIDL接口都要保证对原来的版本的兼容性,避免其它应用无法访问我们的service(其它客户端可能拷贝的还是原来的接口版本)。

下面详细介绍以上几步:

1. 创建.aidl文件

AIDL需要满足一些简单的语法:能够使我们声明一个接口,该接口可以包含一个或多个方法,且能够带有参数和返回值。参数和返回值可以是任何类型的甚至是其它AIDL生成的接口。

每个.aidl文件必须定义一个接口,并且只需要接口声明和方法声明。

默认情况下,AIDL支持以下数据类型:

  • java中八大基本数据类型
  • String
  • CharSequence
  • List 
    当然,List中的类型也需要是AIDL支持的类型。通常使用List声明变量,而具体的类型(例如ArrayList)在定义时确定。
  • Map 
    和List用法一样。

若是使用上述以外的类型(例如自定义类)必须导入相关声明,即使在同一个包中定义的。

在定义AIDL接口时,需要注意以下几点:

  • 方法可以带有0个或多个参数,可以选择有无返回值。
  • 所有的非基本数据类型都需要指定一个方向来标记数据的流向(进,出,进出同时)。基本数据类型默认是进,且不能改变。我们有必要限制数据方向(真正需要的方向),原因在于marshalling参数的代价消耗大。
  • 文件中的所有代码注释都被包含在生成的IBinder接口中,除非是import和package之前注释的。
  • 只支持方法,不支持静态成员变量。且方法不能有修饰符。
  • 需要手动输入包名(android studio不需要手动)

下面有一个例子:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">package</span> com.sywyg.servicetest;

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> com.sywyg.servicetest.Man;
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Declare any non-default types here with import statements</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 需要导入自定义类型的位置</span>
interface IRemoteService{
   <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** Request the process ID of this service, to do evil things with it. */</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> getPid();

    <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> basicTypes(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> anInt, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> aLong, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> aBoolean, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">float</span> aFloat,
              <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">double</span> aDouble, String aString);
     <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Man getMan();</span>
}

</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li></ul>

然后系统就会自动生成一个IRemoteService.java(对应IRemoteService.aidl)文件。 
有的编译器是立刻生成,有的则在编译应用程序时生成,这点注意。

2. 实现接口

自动生成文件包含一个内部抽象类Stub,继承了Binder并是父类接口的一个抽象实现,实现了.aidl文件中的所有方法。Stub同时定义了一些其他有用的方法,尤其是asInterface()方法,该方法接收一个IBinder对象,返回Stub接口的实现。Stub英文表示存根的意思,该类在服务端进程,我们必须继承该类并实现aidl接口中的方法。

下面用一个匿名类实现简单的接口实例:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">    <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
     * 定义一个匿名内部类实现aidl接口,需要继承IRemoteService.Stub类
     * 在.aidl文件中声明的方法需要在这里实现具体功能
     */</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> IRemoteService.Stub mBinder = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> IRemoteService.Stub() {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-title" style="box-sizing: border-box;">getPid</span>(){
            Log.d(TAG,<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"getPid is executed ..."</span>);
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> android.os.Process.myPid();
        }
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">basicTypes</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> anInt, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> aLong, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> aBoolean,
                                   <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">float</span> aFloat, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">double</span> aDouble, String aString) {
            Log.d(TAG,<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"basicTypes is executed ..."</span>);
        }
    };
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li></ul>

Stub实现了Binder类(定义了远程过程调用协议Remote Procedure Call Protocol RPC),因此mBinder可以传输给客户端。

在实现AIDL时需要注意一下几点:

  • 调用不能保证在主线程中执行,我们应该考虑多线程问题,并保证service是线程安全的。
  • 默认情况,RPC调用是异步的。若service需要长时间的操作要保证调用不能发生在主线程中,因为这个可能出现应用程序无法响应问题Application Not Responding ANR。因此我们应该保证调用发生在另外的子线程中。
  • 不会给调用者抛出异常。

3. 将接口暴露给客户端

为service实现了AIDL接口,我们应该把接口暴露给客户端,使得他们能够绑定它。下面给出完整的代码,说明如何实现:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">
<span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
 * 通过AIDL实现IPC
 *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @author</span> sywyg
 *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @since</span> 2015.7.16
 */</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">AIDLService</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Service</span> {</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> String TAG = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"result"</span>;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">AIDLService</span>() {
    }

    <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
     * 定义一个匿名内部类实现aidl接口,需要继承IRemoteService.Stub类
     * 在.aidl文件中声明的方法需要在这里实现具体功能
     */</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> IRemoteService.Stub mBinder = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> IRemoteService.Stub() {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-title" style="box-sizing: border-box;">getPid</span>(){
            Log.d(TAG,<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"getPid is executed ..."</span>);
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> android.os.Process.myPid();
        }
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">basicTypes</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> anInt, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> aLong, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> aBoolean,
                                   <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">float</span> aFloat, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">double</span> aDouble, String aString) {
            Log.d(TAG,<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"basicTypes is executed ..."</span>);
        }
    };

    <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> IBinder <span class="hljs-title" style="box-sizing: border-box;">onBind</span>(Intent intent) {

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> mBinder;
    }
}
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li></ul>

那么,当客户端调用bindService()连接service时,客户端回调onServiceConnected()方法接收mBinder实例(service的onBinder()方法返回的)。

客户端必须也能够获得该接口类,因此当客户端和service在不同的应用程序时,客户端应用程序必须复制一份.aidl文件,这样才能获得AIDL中的方法。

当客户在onServiceConnected()方法中接收IBinder对象时,必须通过调用YourServiceInterface.Stub.asInterface(service)转换为YourServiceInterface类型。如下:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">
IRemoteService mIRemoteService;
  <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> ServiceConnection mConnection = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ServiceConnection() {
        <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onServiceConnected</span>(ComponentName name, IBinder service) {
            Log.d(TAG,<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"绑定成功"</span>);
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Following the example above for an AIDL interface,</span>
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// this gets an instance of the IRemoteService, which we can use to call on the service</span>
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 还是接着上面的例子,通过这种方式获得IRemoteService的一个实例,</span>
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 这样,我们可以在客户端进行处理了。</span>
            mIRemoteService = IRemoteService.Stub.asInterface(service);

            mBound = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>;
        }

        <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onServiceDisconnected</span>(ComponentName name) {
            mBound = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>;
        }
    };
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li></ul>

通过IPC传递对象

我们可以实现通过IPC把对象从一个进程传递到另一个进程中。但是,我们必须要确保在另一个进程中可以获得该对象(即需要该类的代码),并且该类需要支持Parcelable接口。必须要支持Parcelable,这样系统才能将对象分解为基本数据类型(能够跨进程marshalled)。

注意:Parcelable是一个接口,实现该接口的类实例能够保存在Parcel中并从中恢复。该类中必须有一个名叫CREATOR的静态成员变量,该成员是Parcelable.Creator的一个实现实例。

为了创建支持Parcelable协议的类,必须完成以下几点:

  1. 该类必须实现Parcelable接口;
  2. 实现writeToParcel()方法,记录当前对象的状态(成员变量等),并用Parcel保存。还要实现describeContents(),一般返回0;
  3. 添加静态成员变量CREAROR,该成员是Parcelable.Creator的一个实现实例;
  4. 最后创建一个.aidl文件声明该parcelable类(例如下面的Rect.aidl文件)。若你正在进行自定义生成过程,不要添加.aidl文件,这是因为,它类似C语言的头文件,不会进行编译的。???茫然

AIDL通过上述办法产生marshall和unmarshall对象。

下面是一个实现Parcelable接口的类Rect,首先要有Rect.aidl文件:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">package</span> android.graphics;

<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Declare Rect so AIDL can find it and knows that it implements</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// the parcelable protocol.</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 声明Rect,AIDL好找到并确认它实现了parcelable协议</span>
parcelable Rect;
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li></ul>

下面是Rect类:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.os.Parcel;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.os.Parcelable;

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Rect</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">implements</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Parcelable</span> {</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> left;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> top;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> right;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> bottom;

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> Parcelable.Creator<Rect> CREATOR = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span>
Parcelable.Creator<Rect>() {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> Rect <span class="hljs-title" style="box-sizing: border-box;">createFromParcel</span>(Parcel in) {
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Rect(in);
        }

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> Rect[] <span class="hljs-title" style="box-sizing: border-box;">newArray</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> size) {
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Rect[size];
        }
    };

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">Rect</span>() {
    }

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-title" style="box-sizing: border-box;">Rect</span>(Parcel in) {
        readFromParcel(in);
    }

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">writeToParcel</span>(Parcel out) {
        out.writeInt(left);
        out.writeInt(top);
        out.writeInt(right);
        out.writeInt(bottom);
    }

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">readFromParcel</span>(Parcel in) {
        left = in.readInt();
        top = in.readInt();
        right = in.readInt();
        bottom = in.readInt();
    }
}
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li></ul>

Parcel同样可以写其它类型的数据。

警告:不要忘记从另一个进程中获取数据时的安全问题。上面的例子,Rect获取四个数,但是这取决于你获得多少数据(读写顺序要一致)。

更多关于Parcelable,可以参考:Activity之间通信

下面总结一下IPC调用过程

调用IPC

客户端必须完成以下步骤才能实现调用远程接口:

  1. 在项目中包含.aidl文件。
  2. 声明一个IBinder对象实例(基于AIDL产生的)。
  3. 实现ServiceConnection。
  4. 调用Context.bindService(),并传递ServiceConnection的实现。
  5. onServiceConnected()实现中,我们可以接受IBinder的一个实例(名为service)。调用asInterface()转换成接口实例。
  6. 调用在接口中定义的方法。必须要捕获DeadObjectionException异常(当连接断开时),这是调用远程方法的唯一异常。
  7. 调用Context.unBindService()解除连接。

调用IPC service注意事项:

  • 对象是跨进程计数的引用类型,上一章讲的一样,可能会造成内存泄漏。
  • 能够发送匿名对象作为方法参数。
转自http://blog.csdn.net/wangyongge85/article/details/46907599
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android 12 中,C++ ServiceAIDL 和 HAL 仍然是 Android 系统中重要的组成部分。 1. C++ Service C++ ServiceAndroid 系统中的一种基于 Binder 机制实现的服务。它可以在 Android 系统启动时启动,并提供某些系统级别的服务,例如音频服务、电源管理服务等等。 C++ Service 主要的实现语言是 C++,开发者可以使用 C++ 语言来编写自己的 C++ Service。要编写一个 C++ Service,开发者需要实现一个继承自 IInterface 的接口类,并在其中实现自己的服务逻辑。然后,开发者需要通过 ServiceManager 将自己的服务注册到系统中。 2. AIDL AIDLAndroid Interface Definition Language)是 Android 系统中的一种 RPC(Remote Procedure Call)框架,它可以用于在不同的进程之间进行通信。开发者可以使用 AIDL 来定义自己的接口,并通过 Binder 机制将接口暴露给客户端。 AIDL 语言是一种类似于 Java 的语言,开发者可以使用 AIDL 来定义自己的接口、数据类型和异常。在使用 AIDL 时,开发者需要编写一个 AIDL 文件,然后通过 AIDL 工具来生成对应的 Java 接口文件和 C++ 接口文件。在编写服务端和客户端时,开发者需要分别实现 Java 接口和 C++ 接口。 3. HAL HAL(Hardware Abstraction Layer)是 Android 系统中的一种硬件抽象层。它可以将硬件接口和实现分离,使得不同厂商可以实现自己的硬件适配层。开发者可以使用 HAL 来访问硬件设备,例如摄像头、传感器等等。 HAL 主要的实现语言是 C++,开发者可以使用 C++ 语言来编写自己的 HAL。要编写一个 HAL,开发者需要实现自己的 HAL 接口,并在其中实现自己的硬件逻辑。然后,开发者需要将自己的 HAL 注册到系统中,使得其他应用程序可以使用它。 以上就是 Android 12 中 C++ ServiceAIDL 和 HAL 的一些基本介绍。这些技术都是 Android 系统中非常重要的组成部分,开发者需要深入了解它们的使用方法和原理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值