前言
上文中描述了,什么是绑定服务、以及创建一个绑定服务都可以通过哪些方式,同时说了通过扩展Binder类来创建一个绑定服务,并使用一个例子来说明了客户端与服务端的绑定过程,最后又总结了绑定服务的生命周期与调用过程。由于上一篇文章都是在本地应用(单进程)下进行实战的,所以本节主要讲解跨进程通信的主要方式和开发步骤。
创建绑定服务跨进程通信
对于Android 跨进程通信,Android官方提供了两种方式,一种是通过使用 Messenger,一种是使用AIDL。下面摘自官网描述
-
使用 Messenger
如需让接口跨不同进程工作,您可以使用 Messenger 为服务创建接口。采用这种方式时,服务会定义一个 Handler,用于响应不同类型的 Message 对象。此 Handler 是 Messenger 的基础,后者随后可与客户端分享一个 IBinder,以便客户端能利用 Message 对象向服务发送命令。此外,客户端还可定义一个自有 Messenger,以便服务回传消息。
这是执行进程间通信 (IPC) 最为简单的方式,因为 Messenger 会在单个线程中创建包含所有请求的队列,这样您就不必对服务进行线程安全设计。 -
使用 AIDL
Android 接口定义语言 (AIDL) 会将对象分解成原语,操作系统可通过识别这些原语并将其编组到各进程中来执行 IPC。以前采用 Messenger 的方式实际上是以 AIDL 作为其底层结构。如上所述,Messenger 会在单个线程中创建包含所有客户端请求的队列,以便服务一次接收一个请求。不过,如果您想让服务同时处理多个请求,可以直接使用 AIDL。在此情况下,您的服务必须达到线程安全的要求,并且能够进行多线程处理。
如需直接使用 AIDL,您必须创建用于定义编程接口的 .aidl 文件。Android SDK 工具会利用该文件生成实现接口和处理 IPC 的抽象类,您随后可在服务内对该类进行扩展。
注意:大多数应用不应使用 AIDL 来创建绑定服务,因为它可能需要多线程处理能力,并可能导致更为复杂的实现。因此,AIDL 并不适合大多数应用,本文也不会阐述如何将其用于您的服务。如果您确定自己需要直接使用 AIDL,请参阅 AIDL 文档。
使用Messenger
如果您需要让服务与远程进程通信,则可使用 Messenger 为您的服务提供接口。借助此方式,您无需使用 AIDL 便可执行进程间通信 (IPC)。
为接口使用 Messenger 比使用 AIDL 更简单,因为 Messenger 会将所有服务调用加入队列。纯 AIDL 接口会同时向服务发送多个请求,那么服务就必须执行多线程处理。
对于大多数应用,服务无需执行多线程处理,因此使用 Messenger 可让服务一次处理一个调用。如果您的服务必须执行多线程处理,请使用 AIDL 来定义接口。
以下是对 Messenger 使用方式的总结:
- 服务实现一个 Handler,由其接收来自客户端的每个调用的回调。
- 服务使用 Handler 来创建 Messenger 对象(该对象是对 Handler 的引用)。
- Messenger 创建一个 IBinder,服务通过 onBind() 将其返回给客户端。
- 客户端使用 IBinder 将 Messenger(它引用服务的 Handler)实例化,然后再用其将 Message 对象发送给服务。
- 服务在其 Handler 中(具体而言,是在 handleMessage() 方法中)接收每个 Message。
这样,客户端便没有调用服务的方法。相反,客户端会传递服务在其 Handler 中接收的消息(Message 对象)。
Messenger 进行跨进程通信时请求队列是同步进行的,无法并发执行。使用Messenger的例子我们这里就不去验证了,下面我们重点介绍AIDL。
AIDl
AIDL全称Android接口定义语言,利用它可以定义客户端与服务端均认可的编程接口,以便二者使用进程间通信 (IPC) 进行相互通信。在 Android 中,为了在两个不同进程中的内存之间相互进行通信,进程需将其对象分解成可供操作系统理解的原语,并将其编组为可供您操作的对象。编写执行该编组操作的代码较为繁琐,因此 Android 会使用 AIDL 处理此问题。
AIDL可以看做是binder跨进程通信机制在Java层的一种实现方式,在AIDL中定义接口后,由Android sdk 构建之后便可生成Binder的派生类,所以AIDL只是一种工具,为了方便开发而生,所以底层的通信还是基于binder机制,在使用AIDL之前,我们首先来简单熟悉一下Binder与IBinder 。具体的底层原理,我们可在其他文章中体现。
注意:只有在需要不同应用的客户端通过 IPC 方式访问服务,并且希望在服务中进行多线程处理时,您才有必要使用 AIDL。如果您无需跨不同应用执行并发 IPC,则应通过实现 Binder 来创建接口;或者,如果您想执行 IPC,但不需要处理多线程,请使用 Messenger 来实现接口。无论如何,在实现 AIDL 之前,请您务必理解绑定服务。
java中Binder类
远程对象的基类,IBinder定义的轻量级远程过程调用机制的核心部分。该类是IBinder的一个实现,它提供了此类对象的标准本地实现。
我们通常不会直接实现这个类,而是使用aidl工具来描述所需的接口,让它生成适当的Binder子类。 但是,您可以直接从Binder派生来实现您自己的自定义RPC协议,或者简单地直接实例化一个原始Binder对象以用作跨进程共享的令牌。
这个类只是一个基本的IPC原语;它对应用程序的生命周期没有影响,只有在创建它的进程继续运行时才有效。为了正确地使用它,你必须在顶级应用程序组件(Service、Activity或ContentProvider)的上下文中这样做,让系统知道你的进程应该继续运行。
有时候应用进程会被杀掉或消失,此时需要稍后重新创建一个新的Binder,并在进程再次启动时重新附加它。例如,如果你在一个Activity中使用这个,你的Activity进程可能会在Activity没有启动的时候被杀死;如果活动稍后重新创建,您将需要创建一个新的Binder,并再次将其交还到正确的位置;您需要意识到您的进程可能因为其他原因(例如接收广播)而启动,这将不涉及重新创建活动,因此运行其代码来创建一个新的Binder。
java中IBinder 接口
远程对象的基本接口,轻量级远程过程调用机制的核心部分,用于执行进程内和跨进程调用时的高性能。此接口描述了与远程对象交互的抽象协议。不要直接实现这个接口,而是从Binder扩展。
IBinder API的关键是由Binder.onTransact()匹配的transact()。这些方法允许您分别向IBinder对象发送调用和接收进入Binder对象的调用。该事务API是同步的,因此对transaction()的调用直到目标从Binder.onTransact()返回才返回;这是调用本地进程中存在的对象时的预期行为,底层进程间通信(IPC)机制确保在跨进程时应用这些相同的语义。
通过transact()发送的数据是一个Parcel,它是一个通用的数据缓冲区,还维护一些关于其内容的元数据。元数据用于管理缓冲区中的IBinder对象引用,以便在缓冲区跨进程移动时维护这些引用。该机制确保当一个IBinder被写入一个Parcel并发送给另一个进程时,如果另一个进程将对同一个IBinder的引用发送回原始进程,那么原始进程将接收到相同的IBinder对象。这些语义允许将IBinder/Binder对象用作跨进程管理的唯一标识(作为令牌或用于其他目的)。
系统在其运行的每个进程中维护一个事务线程池。这些线程用于分派来自其他进程的所有ipc。例如,当一个IPC从进程A执行到进程B时,A中的调用线程在将事务发送到进程B时阻塞在transact()中。B中的下一个可用池线程接收传入的事务,在目标对象上调用Binder.onTransact(),并返回结果Parcel。在收到结果后,进程A中的线程返回以允许其继续执行。实际上,其他进程似乎使用了您没有在自己的进程中创建并执行的额外线程。
Binder系统还支持跨进程递归。例如,如果进程A向进程B执行了一个事务,而进程B在处理该事务时调用了在A中实现的IBinder上的transact(),那么当前在A中等待原始事务完成的线程将负责在B调用的对象上调用binder . ontransact()。这确保了调用远程绑定器对象时的递归语义与调用本地对象时相同。
在使用远程对象时,您通常希望发现它们何时不再有效。有三种方法可以确定这一点:
如果你试图在一个进程不再存在的IBinder上调用transact()方法,它会抛出一个RemoteException异常。
pingBinder()方法可以被调用,如果远程进程不再存在,它将返回false。
linkToDeath()方法可用于向IBinder注册一个DeathRecipient,当包含它的进程消失时将调用IBinder。
Native层binder 驱动程序
Binder
核心有两个:IPC 、RPC
1) 是一种跨进程通信手段(IPC,Inter-Process Communication)。
2) 是一种远程过程调用手段(RPC,Remote Procedure Call)。
从实现的角度来说,Binder的核心被实现成了一个Linux驱动程序,并运行于内核态。这样它才能具有强大的跨进程访问能力。
如果有两个进程A和B ,A要把内容发给B 那就需要用到IPC,ipc可以看作是一个通道。
RPC 远程过程调用,其实就说调用远程提供的方法,也称为远程函数调用。是在A进程这边创建一些B进程远程调用的接口放到本地,在A进程中封装好数据,通过IPC通道,将数据发送给B,从而在B进程接收数据并调用B的接口完成操作。
IPC是基础,它负责数据传输。要想实现IPC,它需要3个要素,源、目的、数据。其中源指的是进程A,目的指的是进程B。当从A进程发送数据到B时,A并不知道该向哪个进程发送数据,于是,B进程将服务注册到服务管理中心上,接着A进程就向服务管理中心查询需要调用的服务,同时会得到一个handle。后续都通过handle进行通信
而上述的执行过程,都需要借助binder驱动程序来进行通信。
binder机制架构图
binder机制涉及的各个层的类库
使用AIDL
上述贴了几张图让大家大概的了解到了,跨进程通信的复杂性,两个进程之间的通信不仅涉及到了RPC的远程过程调用,又涉及到了用户和内核之间的数据交互,从上层java层到JNI层又到native层可谓是层层调用。而我们现在不需要了解的那么深入,只要知道跨进程通信很复杂就足够了,要想搞清楚binder原理,建议大家直接从binder驱动代码入手直接看,省时省力,光靠理论可是不能完全理解透彻的。但是binder驱动代码可不是谁都能看懂的,首先要理解binder的设计原理,再去从代码层面才能看懂。
关于跨进程通信,Android层面上提供了aidl工具,方便开发者开发接口,通过应用层调用framework层来完成通信。您必须在 .aidl 文件中使用 Java 编程语言的语法定义 AIDL 接口,然后将其保存至应用的源代码(在 src/ 目录中)内,这类应用会托管服务或与服务进行绑定。在构建每个包含 .aidl 文件的应用时,Android SDK 工具会生成基于该 .aidl 文件的 IBinder 接口,并将其保存到项目的 gen/ 目录中。服务必须视情况实现 IBinder 接口。然后,客户端应用便可绑定到该服务,并调用 IBinder 中的方法来执行 IPC。
在腾讯shadow(插件化框架)框架中,主应用与插件应用之间的交互就涉及到了跨进程调用,而且存在主应用与多个插件之间的进程通信,涉及多线程处理。所以此处便用到了aidl生成的代码。下面开始我们看看如何创建一个客户端与服务器,并完成两者之间的交互。
创建aidl绑定服务完成跨进程通信
如要使用 AIDL 创建绑定服务,请执行以下步骤:
-
创建 .aidl 文件
此文件定义带有方法签名的编程接口。此文件定义了客户端和服务端的接口。AIDL 使用一种简单语法,允许您通过一个或多个方法(可接收参数和返回值)来声明接口。参数和返回值可为任意类型,甚至是 AIDL 生成的其他接口。
您必须使用 Java 编程语言构建 .aidl 文件。每个 .aidl 文件均须定义单个接口,并且只需要接口声明和方法签名。 -
实现接口
Android SDK 工具会基于您的 .aidl 文件,使用 Java 编程语言生成接口。此接口拥有一个名为 Stub 的内部抽象类,用于扩展 Binder 类并实现 AIDL 接口中的方法。您必须扩展 Stub 类并实现这些方法。 -
向客户端公开接口
实现 Service 并重写 onBind(),从而返回 Stub 类的实现。
创建aidl文件
在创建aidl文件之前,我们先来了解一下aidl文件的语法:
说是语法应该叫规则更合适,上文中说到,它就是为生成binder派生类而出现的工具文件,Android编译器会根据aidl文件生成java文件,那么这个过程一定会给aidl文件的编写定义一些规范和约束,来指导开发者按照此规约来定义此文件。
同java语言类似,它包括了自己的数据类型,数据类型有两种,一种是基本的数据类型、集合类的数据类型、此外还包括自定义的数据类型如对象形式的。在java中我们定义的基本数据类型如string ,它本身就实现了序列化与反序列化,基本的java类要想实现序列化与反序列化就需要实现serilizable接口。在aidl文件中也是如此。
默认情况下,支持的类型如下
:
-
Java 编程语言中的所有原语类型(如 int、long、char、boolean 等)
-
String
-
CharSequence
-
List
List 中的所有元素必须是以上列表中支持的数据类型,或者您所声明的由 AIDL 生成的其他接口或 Parcelable 类型。您可选择将 List 用作“泛型”类(例如,List)。尽管生成的方法旨在使用 List 接口,但另一方实际接收的具体类始终是 ArrayList。 -
Map
Map 中的所有元素必须是以上列表中支持的数据类型,或者您所声明的由 AIDL 生成的其他接口或 Parcelable 类型。不支持泛型 Map(如 Map<String,Integer> 形式的 Map)。尽管生成的方法旨在使用 Map 接口,但另一方实际接收的具体类始终是 HashMap。
除了上述类型外还有非默认的数据类型,即实现了Parcelable接口的数据类型。
在跨进程通信情况下,由于不同的进程有着不同的内存区域,并且它们只能访问自己的那一块内存区域,所以我们不能像平时那样,传一个句柄过去就完事了——句柄指向的是一个内存区域,现在目标进程根本不能访问源进程的内存,所以我们必须将要传输的数据转化为能够在内存之间流通的形式。这个转化的过程就叫做序列化与反序列化。在传递数据之前,客户端需要对这个对象进行序列化的操作,将其中包含的数据转化为序列化流,然后将这个序列化流传输到服务端的内存中去,再在服务端对这个数据流进行反序列化的操作,从而还原其中包含的数据——通过这种方式,我们就达到了在一个进程中访问另一个进程的数据的目的。在Android中,我们通过AIDL进行跨进程通信的时候,选择的序列化方式是实现 Parcelable 接口。
接口定义
定义服务接口时,请注意:
- 方法可带零个或多个参数,返回值或空值。
- 所有非原语参数均需要指示数据走向的方向标记。这类标记可以是 in、out 或 inout(见下方示例)。原语默认为 in,不能是其他方向。
此标记表示了在跨进程通信中数据的流向,其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。其中,数据流向是针对在客户端中的那个传入方法的对象而言的。in 为定向 tag 的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;out 的话表现为服务端将会接收到那个对象的的空对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。
注意:您应将方向限定为真正需要的方向,因为编组参数的开销较大。
- 生成的 IBinder 接口内包含 .aidl 文件中的所有代码注释(import 和 package 语句之前的注释除外)。
- 您可以在 ADL 接口中定义 String 常量和 int 字符串常量。例如:const int VERSION = 1;。
- 方法调用由 transact() 代码分派,该代码通常基于接口中的方法索引。由于这会增加版本控制的难度,因此您可以向方法手动配置事务代码:void method() = 10;。
- 使用 @nullable 注释可空参数或返回类型。
1、按照以上步骤,我们来创建一个aidl:在Android studio中如下图所示:
一步操作,一个aidl文件就已经创建了。创建完成后会自动创建一个aidl文件夹,并生成一个aidl文件如下图:
2、在aidl文件中定义两个测试接口,
// 定义方法,此方法想从服务端拿文件字符串
String getFile();
//发送数据,此方法想向服务端发送消息,并需要服务端返回消息
String sendData(String message);**
aidl文件如下:
// IMyAidlInterface.aidl
package com.cop.ronghw.study_android_exact;// Declare any non-default types here with import statementsinterface IMyAidlInterface {/*** Demonstrates some basic types that you can use as parameters* and return values in AIDL.*/void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,double aDouble, String aString);// 定义方法String getFile();//发送数据String sendData(String message);
}
3、构建应用:构建项目,Android SDK 工具会生成以 .aidl 文件命名的 .java 接口文件。如下图所示:
分析由aidl文件生成的同名java文件
上面我们已经通过aidl文件生成了java文件,这个java文件怎么使用呢?首先我们先来分析一下此java文件。由于生成的java文件代码比较长,这里我们来分段解析:首先贴一下代码:
/** This file is auto-generated. DO NOT MODIFY.*/
package com.cop.ronghw.study_android_exact;
// Declare any non-default types here with import statementspublic interface IMyAidlInterface extends android.os.IInterface {/*** Default implementation for IMyAidlInterface.*/public static class Default implements com.cop.ronghw.study_android_exact.IMyAidlInterface {/*** Demonstrates some basic types that you can use as parameters* and return values in AIDL.*/@Overridepublic void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {}// 定义方法@Overridepublic java.lang.String getFile() throws android.os.RemoteException {return null;}//发送数据@Overridepublic java.lang.String sendData(java.lang.String message) throws android.os.RemoteException {return null;}@Overridepublic android.os.IBinder asBinder() {return null;}}/*** Local-side IPC implementation stub class.*/public static abstract class Stub extends android.os.Binder implements com.cop.ronghw.study_android_exact.IMyAidlInterface {private static final java.lang.String DESCRIPTOR = "com.cop.ronghw.study_android_exact.IMyAidlInterface";/*** Construct the stub at attach it to the interface.*/public Stub() {this.attachInterface(this, DESCRIPTOR);}/*** Cast an IBinder object into an com.cop.ronghw.study_android_exact.IMyAidlInterface interface,* generating a proxy if needed.*/public static com.cop.ronghw.study_android_exact.IMyAidlInterface asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin != null) && (iin instanceof com.cop.ronghw.study_android_exact.IMyAidlInterface))) {return ((com.cop.ronghw.study_android_exact.IMyAidlInterface) iin);}return new com.cop.ronghw.study_android_exact.IMyAidlInterface.Stub.Proxy(obj);}@Overridepublic android.os.IBinder asBinder() {return this;}@Overridepublic boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {java.lang.String descriptor = DESCRIPTOR;switch (code) {case INTERFACE_TRANSACTION: {reply.writeString(descriptor);return true;}case TRANSACTION_basicTypes: {data.enforceInterface(descriptor);int _arg0;_arg0 = data.readInt();long _arg1;_arg1 = data.readLong();boolean _arg2;_arg2 = (0 != data.readInt());float _arg3;_arg3 = data.readFloat();double _arg4;_arg4 = data.readDouble();java.lang.String _arg5;_arg5 = data.readString();this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);reply.writeNoException();return true;}case TRANSACTION_getFile: {data.enforceInterface(descriptor);java.lang.String _result = this.getFile();reply.writeNoException();reply.writeString(_result);return true;}case TRANSACTION_sendData: {data.enforceInterface(descriptor);java.lang.String _arg0;_arg0 = data.readString();java.lang.String _result = this.sendData(_arg0);reply.writeNoException();reply.writeString(_result);return true;}default: {return super.onTransact(code, data, reply, flags);}}}private static class Proxy implements com.cop.ronghw.study_android_exact.IMyAidlInterface {private android.os.IBinder mRemote;Proxy(android.os.IBinder remote) {mRemote = remote;}@Overridepublic android.os.IBinder asBinder() {return mRemote;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}/*** Demonstrates some basic types that you can use as parameters* and return values in AIDL.*/@Overridepublic void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeInt(anInt);_data.writeLong(aLong);_data.writeInt(((aBoolean) ? (1) : (0)));_data.writeFloat(aFloat);_data.writeDouble(aDouble);_data.writeString(aString);boolean _status = mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);if (!_status && getDefaultImpl() != null) {getDefaultImpl().basicTypes(anInt, aLong, aBoolean, aFloat, aDouble, aString);return;}_reply.readException();} finally {_reply.recycle();_data.recycle();}}// 定义方法@Overridepublic java.lang.String getFile() 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);boolean _status = mRemote.transact(Stub.TRANSACTION_getFile, _data, _reply, 0);if (!_status && getDefaultImpl() != null) {return getDefaultImpl().getFile();}_reply.readException();_result = _reply.readString();} finally {_reply.recycle();_data.recycle();}return _result;}//发送数据@Overridepublic java.lang.String sendData(java.lang.String message) 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);_data.writeString(message);boolean _status = mRemote.transact(Stub.TRANSACTION_sendData, _data, _reply, 0);if (!_status && getDefaultImpl() != null) {return getDefaultImpl().sendData(message);}_reply.readException();_result = _reply.readString();} finally {_reply.recycle();_data.recycle();}return _result;}public static com.cop.ronghw.study_android_exact.IMyAidlInterface sDefaultImpl;}static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_getFile = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);static final int TRANSACTION_sendData = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);public static boolean setDefaultImpl(com.cop.ronghw.study_android_exact.IMyAidlInterface impl) {// Only one user of this interface can use this function// at a time. This is a heuristic to detect if two different// users in the same process use this function.if (Stub.Proxy.sDefaultImpl != null) {throw new IllegalStateException("setDefaultImpl() called twice");}if (impl != null) {Stub.Proxy.sDefaultImpl = impl;return true;}return false;}public static com.cop.ronghw.study_android_exact.IMyAidlInterface getDefaultImpl() {return Stub.Proxy.sDefaultImpl;}}/*** Demonstrates some basic types that you can use as parameters* and return values in AIDL.*/public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;// 定义方法public java.lang.String getFile() throws android.os.RemoteException;//发送数据public java.lang.String sendData(java.lang.String message) throws android.os.RemoteException;
}
上述代码看起来比较多,我们把代码收缩贴图看下:
上图中,包括了我们在创建aidl文件时自定义的接口方法,还包括了两个静态内部类。
两个方法就是我们在定义aild文件的时候自定义的。静态内部类中的 Default类可先忽略,重点在于抽象类Stub。
在文章前面我们说了,跨进程通信,必然有一方作为客户端,一方作为服务端,两端交互就必然规定了一些交互协议,就类似我们熟悉的webservice,webservice的协议使用soap+http,客户端和服务端通过定义命名空间和接口方法来保证两端的通信,因为webservice同样也属于RPC的调用。同理,Android中使用的协议上层使用的是IBinder定义的协议规范,IBinder接口也遵循了底层binder驱动定义的系统调用接口规范,同样也属于RPC调用,也会经过一系列的序列化与反序列的过程。理解了这些,对我们分析这个生成的java类就大有帮助了。
所以,刚刚我们定义的方法 (sendData 和 getFile) 就是用来客户端与服务端交互的方法,即客户端调用了sendData方法,就会封装数据,通过binder驱动调用到了服务端的sendData方法。所以我们同样需要在服务端实现这两个方法,在服务端这两个方法就是涉及了实际的业务逻辑。同样的在客户端也会有这两个方法,客户端这两个方法的作用就是远程调用中需要传哪些参数,构造哪些数据会打个包给服务端。然而看似简单的调用过程,中间过程却有一系列的操作,毕竟要做到两个不同的内存之间相互访问不是简单的事情。Android 提供了binder机制就是做这个事情。
总结:在aidl生成的java文件中提供了客户端和服务端的标准,提供了客户端和服务端的交互机制。
Stub:
紧接上述的Stub类继续分析,Stub类扩展了IMyAidlInterface接口,在Stub类中定义了asInterface方法,这个方法的作用是用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,这种转换过程是区分进程的【如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的Stub对象本身,否则返回的是系统封装后的Stub.proxy对象】 简单来说,就是对于不同进程返回了Proxy对象,即这个对象就是给客户端指明了具体调用什么,也就是说这个 asInterface 方法返回的是一个远程接口具备的能力(有什么方法可以调用)。
/*** Cast an IBinder object into an com.cop.ronghw.study_android_exact.IMyAidlInterface interface,* generating a proxy if needed.*/public static com.cop.ronghw.study_android_exact.IMyAidlInterface asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin != null) && (iin instanceof com.cop.ronghw.study_android_exact.IMyAidlInterface))) {return ((com.cop.ronghw.study_android_exact.IMyAidlInterface) iin);}return new com.cop.ronghw.study_android_exact.IMyAidlInterface.Stub.Proxy(obj);}
Proxy
接下来再简单分析一下Stub.Proxy类,此代理类的作用是构造数据、封装数据,将数据发送给server服务端。代理类Proxy实现了IMyAidlInterface接口,并实现了我们刚刚说的静态方法,如果客户端想使用getFile方法,必须先经过代理Proxy,在Proxy的getFile方法中封装数据,通过IBinder的引用发起IPC调用,并到服务端的方法。
private static class Proxy implements com.cop.ronghw.study_android_exact.IMyAidlInterface {private android.os.IBinder mRemote;Proxy(android.os.IBinder remote) {mRemote = remote;}@Overridepublic android.os.IBinder asBinder() {return mRemote;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}/*** Demonstrates some basic types that you can use as parameters* and return values in AIDL.*/@Overridepublic void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeInt(anInt);_data.writeLong(aLong);_data.writeInt(((aBoolean) ? (1) : (0)));_data.writeFloat(aFloat);_data.writeDouble(aDouble);_data.writeString(aString);boolean _status = mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);if (!_status && getDefaultImpl() != null) {getDefaultImpl().basicTypes(anInt, aLong, aBoolean, aFloat, aDouble, aString);return;}_reply.readException();} finally {_reply.recycle();_data.recycle();}}// 定义方法@Overridepublic java.lang.String getFile() 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);boolean _status = mRemote.transact(Stub.TRANSACTION_getFile, _data, _reply, 0);if (!_status && getDefaultImpl() != null) {return getDefaultImpl().getFile();}_reply.readException();_result = _reply.readString();} finally {_reply.recycle();_data.recycle();}return _result;}//发送数据@Overridepublic java.lang.String sendData(java.lang.String message) 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);_data.writeString(message);boolean _status = mRemote.transact(Stub.TRANSACTION_sendData, _data, _reply, 0);if (!_status && getDefaultImpl() != null) {return getDefaultImpl().sendData(message);}_reply.readException();_result = _reply.readString();} finally {_reply.recycle();_data.recycle();}return _result;}public static com.cop.ronghw.study_android_exact.IMyAidlInterface sDefaultImpl;}
onTransact
方法:onTransact方法 运行在服务端中的 Binder 线程池中。客户端发起跨进程请求时,远程请求会通过系统底层封装后交给此方法来处理。如果此方法返回 false,那么客户端的请求就会失败。此方法包含了以下参数:
- code : 确定客户端请求的目标方法是什么。即确定客户端调用哪个服务端的实际处理业务的方法,比如getFile或sendData方法
- data : 如果目标方法有参数的话,就从data取出目标方法所需的参数。
- reply :当目标方法执行完毕后,如果目标方法有返回值,就向reply中写入返回值。
- flag:附加操作标志。对于普通RPC,可以是0,对于单向RPC,可以是FLAG_ONEWAY
也就是说,这个 onTransact方法 就是服务端处理的核心,接收到客户端的请求,并且通过客户端携带的参数,执行完服务端的方法,返回结果。
到此为止我们分析了Stub类的作用,通过分析可发现,通过aidl生成的java文件必须都包括在客户端和服务端代码中,两端都必须有这个文件。这个文件本文只是描述了大概,只是为了让大家清楚此文件的作用和如何发挥作用的。具体的交互过程还是要看binder底层驱动,由于本人能力有限,这个后面再去分析学习,然后再分享给大家。
到目前为止aidl文件就先分析到这里吧。下文我们再通过客户端与服务端代码案例来穿插讲解aidl生成的java文件如何使用的。
总结
本文主要介绍了跨进程通信涉及的技术点,也着重分析了跨进程通信的几种方式,并让大家简单了解了binder的机制原理,紧接着重点介绍了aidl文件的使用步骤及创建方式,又介绍了aidl文件的语法及做用。最后重点介绍了aidl文件如何生成java文件,并简要分析了此java文件在客户端与服务端之间的使用过程,及部分系统调用原理。
下文我们会通过客户端和服务端跨进程的案例来进一步了解通信机制。