跨进程通信机制
跨进程通信机制
1. Binder
1.1. Binder定义
Binder是Android系统中进程间通讯(IPC)的一种方式,也是Android系统中最重要的特性之一。Android中的四大组件Activity,Service,Broadcast,ContentProvider,不同的App等都运行在不同的进程中,它是这些进程间通讯的桥梁。
1.2. Binder架构
Binder跨进程通信机制模型基于Client - Server 模式,其在 framework 层进行了封装,通过 JNI 技术调用 Native(C/C++)层的 Binder 架构。而在Native层,Binder 则以 ioctl 的方式与 Binder 驱动进行通讯,其架构图如下:
Binder架构中的组件主要包括 Client、 Server、 ServiceManager 以及 Binder 驱动四种,各角色作用如下:
| 角色 | 作用 |
|---|---|
| Client | Android客户端,使用服务的进程 |
| Server | 服务器端,提供服务的进程 |
| ServiceManager | 管理Service的注册与查询,将字符形式的Binder名字转化成Client中对该Binder的代理 |
| Binder驱动 | 负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持 |
1.3. Binder机制
Binder机制如下图所示:
- 首先需要注册
Server端,只有注册了Server端,Client端才有通讯的目标,Server端通过ServiceManager注册服务,注册的过程就是向 Binder 驱动的全局链表binder_procs中插入Server端的信息(binder_proc是结构体,每个binder_proc结构体中都有todo任务队列),然后向ServiceManager的svcinfo列表中缓存注册的服务; - 在
Server端注册完成后,Client端就可以与其进行通讯了。在通讯之前Client端需要先获取服务,即拿到服务的代理,也可以理解为引用。获取Server端的方式就是通过ServiceManager到svcinfo列表中查询所需服务并返回Server端的代理,svcinfo列表就是所有已注册服务的通讯录,保存了所有已注册服务的信息; - 在有了
Server端的代理之后即可向Server端发送请求。Client端通过BinderProxy将请求参数发送给ServiceManager,通过共享内存的方式使用内核方法copy_from_user()将请求参数先拷贝到内核空间,此时Client端进入等待状态,然后 Binder 驱动向Server端的todo队列里面插入一条事务,执行完成之后通过copy_to_user()将内核的执行结果拷贝到用户空间(这里只执行拷贝命令,并没有拷贝数据),唤醒等待的客户端并把结果返回,即完成了一次通讯。
1.4. Binder驱动
Linux 内核的运行空间与用户程序的运行空间是相互隔离的,即使用户程序崩溃了,内核也不受影响。内核空间与用户空间的交互过程如下图所示:
内核空间可以执行任意命令,调用系统的一切资源,而用户空间只能执行简单的运算,不能直接调用系统资源。虽然从逻辑上抽离出内核空间和用户空间,但是不可避免的的是,总有那么一些用户空间需要访问内核的资源。而用户空间访问内核空间的唯一方式就是系统调用(System call),通过这个统一的入口接口,所有的资源访问都是在内核的控制下执行,以免导致用户程序对系统资源的越权访问,从而保障了系统的安全和稳定。
当一个任务(进程)执行系统调用而陷入内核代码中执行时,就称进程处于内核运行态(或简称为内核态),此时处理器处于特权级最高的(0级)内核代码中执行,处理器在特权等级高的时候才能执行特权CPU指令。当进程在执行用户自己的代码时,则称其处于用户运行态(用户态),即此时处理器在特权级最低的(3级)用户代码中运行。
通过系统调用,用户空间可以访问内核空间,而当一个用户空间想与另外一个用户空间进行通信时就需要让操作系统内核添加支持,如Socket、管道等都是内核支持的。但 Binder 并不是 Linux 内核的一部分,而是 Linux 的动态可加载内核模块(Loadable Kernel Module,LKM)。Binder是具有独立功能的程序,可以被单独编译,但不能独立运行。它在运行时被链接到内核作为内核的一部分在内核空间运行。因此,通过添加一个运行在内核空间的内核模块,作为用户进程之间通信的桥梁,从而实现进程间通信。在 Android 系统中,这个运行在内核空间的,负责各个用户进程通过 Binder 通信的内核模块叫做 Binder 驱动;
在上图中,用户空间中 binder_open(), binder_mmap(), binder_ioctl()方法通过 System call 来调用内核空间 Binder 驱动中相对应的方法。内核空间与用户空间的共享内存通过 copy_from_user()和 copy_to_user()内核方法来完成用户空间与内核空间的数据传输。此外,Binder 驱动中有一个全局的 binder_procs 链表,用来保存Server端的进程信息。
1.5. Binder进程与线程
对于底层Binder驱动而言,通过 binder_procs 链表记录所有创建的 binder_proc 结构体,Binder 驱动中的每一个 binder_proc 结构体都与用户空间中的一个用 Binder 通信的进程相对应,且每个进程有且只有一个 ProcessState 对象,通过单例模式实现。在每个进程中可以有多个线程,每个线程对应一个 IPCThreadState 对象,IPCThreadState 对象也是通过单例模式实现,在 Binder 驱动层也有与之相对应的结构,即Binder_thread 结构体。在 binder_proc 结构体中通过成员变量 rb_root threads来记录当前进程内所有的 binder_thread。
每个 Server 进程在启动时创建一个 Binder 线程池,并向其中注册一个 Binder 线程,之后 Server 进程也可以向 binder 线程池注册新的线程。当 Binder 驱动在探测到没有空闲 binder 线程时,也可以主动向 Server 进程池注册新的的 binder 线程。对于一个 Server 进程有一个最大 Binder 线程数限制,默认为16个 Binder 线程。对于所有 Client 端进程的 Binder 请求都是交由 Server 端进程的 Binder 线程来处理的。
1.6. ServiceManager启动
ServiceManager提供注册服务与查询服务的功能,其启动如下图所示:
ServiceManager 分为 framework 层和 native 层,framework 层只是对 native 层进行了封装方便调用,图上展示的是 native 层的 ServiceManager 的启动过程。
ServiceManager 的启动是系统在开机时,init 进程解析 init.rc 文件并调用 service_manager.c 中的 main() 方法启动的。 native 层的 binder.c 封装了一些与 Binder 驱动交互的方法。
ServiceManager 的启动分为三步:首先打开驱动创建全局链表 binder_procs;然后将自己当前进程信息保存到 binder_procs 链表;最后开启 loop 不断的处理共享内存中的数据,并处理 BR_xxx 命令(ioctl 的命令)。
1.7. ServiceManager 注册服务
Service组件运行在Server进程中,首先要将Service注册到Service Manager中,再启动一个Binder线程池来等待和处理Client端的通信请求。
注册过程(addService)的核心工作是在服务所在进程创建binder_node,在ServiceManager进程创建binder_ref。
以Media服务为例,注册的过程涉及到MediaPlayerService(作为Client进程)和Service Manager(作为Service进程),通信流程图如下所示:
- 注册
MediaPlayerService服务端,通过 ServiceManager 的addService()方法来注册服务; - 首先 ServiceManager 向 Binder 驱动发送
BC_TRANSACTION命令,并携带ADD_SERVICE_TRANSACTION命令,同时注册服务的线程进入等待状态waitForResponse()。 Binder 驱动收到请求命令向 ServiceManager 的todo队列里面添加一条注册服务的事务。事务的任务就是创建服务端进程binder_node信息并插入到binder_procs链表中; - 事务处理完之后发送
BR_TRANSACTION命令,ServiceManager 收到命令后向svcinfo列表中添加已经注册的服务。最后发送BR_REPLY命令唤醒等待的线程,通知注册成功。
1.8. ServiceManager 获取服务
请求服务过程,就是向serviceManager进程查询指定服务,当执行binder_transaction()时,会区分请求服务所属进程情况:
- 当请求服务的进程与服务属于不同进程,则为请求服务所在进程创建
binder_ref对象,指向服务进程中的binder_node,即返回请求服务的一个代理对象; - 当请求服务的进程与服务属于同一进程,则不再创建新对象,而是返回的对象的真实子类;
ServiceManager 获取服务的流程如下图所示:
- 获取服务的过程与注册类似,相反的过程。通过 ServiceManager 的
getService()方法来注册服务; - 首先 ServiceManager 向 Binder 驱动发送
BC_TRANSACTION命令,并携带CHECK_SERVICE_TRANSACTION命令,同时获取服务的线程进入等待状态waitForResponse(); - Binder 驱动收到请求命令向 ServiceManager 的发送
BC_TRANSACTION查询已注册的服务,若查询到所需服务则直接响应BR_REPLY并唤醒等待的线程,否则将与binder_procs链表中的服务进行一次通讯再响应。
1.9. 进行一次完整通讯
进行一次完整通讯的流程如下图所示:
- 服务注册完成;
- 首先通过 ServiceManager 获取到服务端的
BinderProxy代理对象,通过调用BinderProxy将参数,方法标识传给 ServiceManager,同时客户端线程进入等待状态; - ServiceManager 将用户空间的参数等请求数据复制到内核空间,并向服务端插入一条执行方法的事务。事务执行完通知 ServiceManager 将执行结果从内核空间复制到用户空间,并唤醒等待的线程,响应结果,通讯结束。
2. AIDL
2.1. 概述
AIDL,全称是 “Android Interface Definition Language”,也就是 “Android接口定义语言”。设计这门语言的目的是为了实现进程间通信,尤其是在涉及多进程并发情况下的进程间通信。
在 Android 中,一个进程通常无法访问另一个进程的内存。因此,为进行通信,进程需将其对象分解成可供操作系统理解的原语,并将其编组为可供操作的对象。而通过 AIDL 即可定义客户端与服务端均认可的编程接口,以便二者之间实现通信。
2.2. AID语法
AIDL 的语法基本上和 Java 是一样的,只是在一些细微处有些许差别,差别之处主要如下所示:
-
文件类型:用 AIDL 书写的文件的后缀是**
.aidl**,而不是.java。 -
数据类型:AIDL 默认支持一些数据类型,在使用这些数据类型的时候是不需要导包的。但是除了这些类型之外的数据类型,在使用之前必须导包,即使目标文件与当前正在编写的 .aidl 文件在同一个包下也是需要导包的(在 Java 中,这种情况是不需要导包的)。
- 默认支持的数据类型包括:
- Java中的八种基本数据类型:boolean,byte,char,short,int,float,double,long;
- String 类型;
- CharSequence 类型;
- List 类型:List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable,List可以使用泛型;
- Map 类型:Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable,Map是不支持泛型的。
- 默认支持的数据类型包括:
-
定向tag:AIDL 中的定向 tag 表示了在跨进程通信中数据的流向,其中 in 表示数据只能由客户端流向服务端; out 表示数据只能由服务端流向客户端;而 inout 则表示数据可在服务端与客户端之间双向流通。其中,数据流向是针对在客户端中的那个传入方法的对象而言的。in 为定向 tag 的话表现为服务端将会接收到一个客户端中传入方法的对象的完整数据,但是客户端该对象不会因为服务端对传参的修改而发生变动;out 的话表现为服务端将会接收到那个对象的的空对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。
注:Java 中的基本类型和 String ,CharSequence 的定向 tag 默认且只能是 in。
-
两类AIDL文件:第一类是用来定义 parcelable 对象,以供其他 AIDL 文件使用 AIDL 中非默认支持的数据类型的。第二类是用来定义方法接口,以供系统使用来完成跨进程通信的。两类文件都是在“定义”,而不涉及具体的实现,这就是为什么它叫做“Android接口定义语言”。
注:所有的非默认支持数据类型必须通过第一类AIDL文件定义 parcelable 对象才能被使用。
举例如下:
目录结构如图所示:

其中,Pet.java、Person.java分别与Pet.aidl、Person.aidl对应,对应的java文件与aidl文件的包名需一致(自动编译)。
Pet.aidl、Person.aidl以及Ipet.aidl文件代码如下:
package com.example.aidlparcelableservertest;// Pet.aidl
// 第一类AIDL文件
// 这个文件的作用是引入一个序列化对象 Pet 供其他的AIDL文件使用
// Pet.aidl 与 Pet.java的包名应该是一样的
parcelable Pet;
package com.example.aidlparcelableservertest;// Person.aidl
// 第一类AIDL文件
// 这个文件的作用是引入一个序列化对象 Person 供其他的AIDL文件使用
// Person.aidl 与 Person.java的包名应该时一样的
parcelable Person;
package com.example.aidlparcelableservertest;// Ipet.aidl
// 第二类AIDL文件// Pet和Person不是默认支持的类型
// 即使Ipet.aidl与Pet.aidl、Person.aidl在同一包内,也需要导入
import com.example.aidlparcelableservertest.Pet;
import com.example.aidlparcelableservertest.Person;interface IPet {// 无论是什么类型,所有的返回值前都不需要加任何东西// 定义一个Person对象作为传入参数List<Pet> getPets(in Person owner);// 传参时除Java基本类型void addPetIn(in Pet pet);void addPetOut(out Pet pet);void addPetInout(inout Pet pet);}
2.3. 如使用AIDL实现跨进程通信
在进行跨进程通信时,AIDL 中定义的方法里包含非默认支持的数据类型与否,要进行的操作是不一样的。如果不包含,则只需要编写一个.aidl 文件(第二类),如果包含,那么我们通常需要写 n+1 个 .aidl 文件( n 为非默认支持的数据类型的种类数)。所以接下来以 AIDL 文件中包含非默认支持的数据类型为例进行介绍。
在构建每个包含.aidl文件的应用时,Android SDK 工具会生成基于该.aidl 文件的IBinder接口,并将其保存到项目的 app/build/generated/aidl_source_output_dir 目录中。服务必须视情况实现IBinder 接口。然后,客户端应用便可绑定到该服务,并调用IBinder 中的方法来执行 IPC。
使用 AIDL 创建绑定服务的步骤如下:
- 创建
.aidl文件:- 此文件定义带有方法签名的编程接口。
- 实现接口:
- Android SDK 工具基于
.aidl文件,使用 Java 编程语言生成接口。此接口拥有一个名为Stub的内部抽象类,用于扩展Binder类并实现 AIDL 接口中的方法,必须扩展该Stub类并实现这些方法。
- Android SDK 工具基于
- 向客户端公开接口:
- 实现
Service并重写onBind(),从而返回Stub类的实现。
- 实现
2.3.1. 创建 .aidl文件
创建Pet.aidl、Person.aidl文件,代码如下:
package com.example.aidlparcelableservertest;parcelable Pet;
package com.example.aidlparcelableservertest;parcelable Person;
定义接口服务时注意:
- 方法可带零个或多个参数,返回值或空值;
- 所有非默认支持的数据类型的参数均需要指示数据流向的方向标记:
in、out或inout。默认支持的数据类型的参数只能为in; - 可以在 ADL 接口中定义 String 常量和 int 字符串常量,如:
const int VERSION = 1;
2.3.2. 实现 Parcelable 接口
可以通过 IPC 接口,将某个类从一个进程发送至另一个进程。但必须确保 IPC 通道的另一端可使用该类的代码,并且该类必须支持 Parcelable 接口。
实现 Parcelable 接口相当于 Android 提供的一种自定义序列化机制,不仅要求实现Parcelable接口中定义的方法,而且要求在实现类中定义一个名为CREATOR、类型为Parcelable.Creator的静态常量。
- 创建与
Pet.aidl、Person.aidl对应的文件Pet.java、Person.java,对应的java文件与aidl文件的包名一致。Pet.aidl、Person.aidl中的内容分别如下所示:
package com.example.aidlparcelableservertest;import android.os.Parcel;
import android.os.Parcelable;public class Pet implements Parcelable {private String name;private double weight;public Pet() {}public Pet(String name, double weight) {super();this.name = name;this.weight = weight;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getWeight() {return weight;}public void setWeight(double weight) {this.weight = weight;}// 实现Parcelable接口必须实现的方法@Overridepublic int describeContents() {return 0;}// 实现Parcelable接口必须实现的方法// 将Pet对象的数据写入到parcel中@Overridepublic void writeToParcel(Parcel parcel, int i) {parcel.writeString(name);parcel.writeDouble(weight);}// 添加一个静态成员CREATOR,该对象实现了Parcelable.Creator接口// 该静态常量的值负责从parcel数据包中回复Pet对象public static final Creator<Pet> CREATOR = new Creator<Pet>() {@Overridepublic Pet createFromParcel(Parcel parcel) {// 从Parcel中读取数据,返回Pet对象return new Pet(parcel.readString(), parcel.readDouble());}@Overridepublic Pet[] newArray(int i) {return new Pet[i];}};@Overridepublic String toString() {return "Pet{" + "name='" + name + ", weight=" + weight + '}';}
}
package com.example.aidlparcelableservertest;import android.os.Parcel;
import android.os.Parcelable;import java.util.Objects;public class Person implements Parcelable {private int id;private String name;private String pass;public Person() {}public Person(Integer id, String name, String pass) {super();this.id = id;this.name = name;this.pass = pass;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPass() {return pass;}public void setPass(String pass) {this.pass = pass;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Person person = (Person) o;return Objects.equals(name, person.name) && Objects.equals(pass, person.pass);}@Overridepublic int hashCode() {return Objects.hash(id, name, pass);}// 实现Parcelable接口必须实现describeContents@Overridepublic int describeContents() {return 0;}// 实现Parcelable接口必须实现describeContents// 将Person对象的数据写入到parcel中@Overridepublic void writeToParcel(Parcel parcel, int i) {// 把该对象所包含的数据写到Parcel中parcel.writeInt(id);parcel.writeString(name);parcel.writeString(pass);}// 添加一个静态成员CREATOR,该对象实现了Parcelable.Creator接口// 该静态常量的值负责从parcel数据包中回复Person对象public static final Creator<Person> CREATOR = new Creator<Person>() {@Overridepublic Person createFromParcel(Parcel parcel) {// 从Parcel中读取数据,返回Person对象return new Person(parcel.readInt(), parcel.readString(), parcel.readString());}@Overridepublic Person[] newArray(int i) {return new Person[i];}};
}
注:若AIDL文件中涉及到的所有数据类型均为默认支持的数据类型,则无此步骤。因为默认支持的数据类型都是可序列化的。
- 创建通信接口的
Ipet.aidl文件,代码如下:
package com.example.aidlparcelableservertest;import com.example.aidlparcelableservertest.Pet;
import com.example.aidlparcelableservertest.Person;interface IPet {List<Pet> getPets(in Person owner);}
在定义完该通信接口的 AIDL 文件之后,在项目的 app/build/generated/aidl_source_output_dir 目录中会生成基于该.aidl 文件的IBinder接口(未生成则点击Make Project)。接口文件分析见后文。
2.3.3. 向客户端公开接口
- 实现
ParcelableService类并重写onBind(),从而返回Stub类的实现,代码如下:
package com.example.aidlparcelableservertest;import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class ParcelableService extends Service {private static final String TAG = "ParcelableService";private PetBinder petBinder;private static Map<Person, List<Pet>> pets = new HashMap<>();static {// 初始化pets Map集合List<Pet> list1 = new ArrayList<>();list1.add(new Pet("旺财", 4.3));list1.add(new Pet("来福", 5.8));pets.put(new Person(1, "sun", "1234"), list1);List<Pet> list2 = new ArrayList<>();list2.add(new Pet("kitty", 6.6));list2.add(new Pet("bobby", 8.8));pets.put(new Person(2, "moon", "4321"), list2);}// 继承Stub,也就是实现了IPet接口,并实现了IBinder接口class PetBinder extends IPet.Stub {// getPets()方法的实现@Overridepublic List<Pet> getPets(Person owner) throws RemoteException {return pets.get(owner);}}@Overridepublic void onCreate() {super.onCreate();petBinder = new PetBinder();}@Overridepublic IBinder onBind(Intent intent) {/*** 返回petBinder对象* 在绑定本地Service的情况下,该petBinder对象会直接传给客户端的ServiceConnection对象的onServiceConnected方法的第二个参数* 在绑定远程Service的情况下,只将petBinder对象的代理传给客户端的ServiceConnection对象的onServiceConnected方法的第二个参数*/return petBinder;}
}
- 创建客户端进程
MainActivity.java,代码如下。客户端必须拥有接口类的访问权限,因此如果客户端和服务在不同应用内,则客户端应用的src/目录内必须包含.aidl文件(该文件会生成android.os.Binder接口,进而为客户端提供 AIDL 方法的访问权限)的副本,即将Pet与Person类的java文件和AIDL文件以及IPet.aidl复制到客户端中。注意,复制时需要保持包名不变。

package com.example.aidlparcelableclienttest;import androidx.appcompat.app.AppCompatActivity;import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;// 引入IPet.aidl及Pet.java、Person.java
import com.example.aidlparcelableservertest.IPet;
import com.example.aidlparcelableservertest.Pet;
import com.example.aidlparcelableservertest.Person;import java.util.List;public class MainActivity extends AppCompatActivity {private IPet petService;private ServiceConnection conn = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName componentName, IBinder iBinder) {// 获取远程Service的onBind方法所返回的对象的代理petService = IPet.Stub.asInterface(iBinder);}@Overridepublic void onServiceDisconnected(ComponentName componentName) {petService = null;}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);EditText personView = findViewById(R.id.person);ListView showView = findViewById(R.id.show);Button getBtn = findViewById(R.id.get);Intent intent = new Intent();intent.setAction("PARCELABLE_SERVICE");intent.setPackage("com.example.aidlparcelableservertest");bindService(intent, conn, Service.BIND_AUTO_CREATE);getBtn.setOnClickListener(view -> {String personName = personView.getText().toString();try {// 调用远程Service方法List<Pet> pets = petService.getPets(new Person(1, personName, personName));// 将程序返回的list包装成ArrayAdapterArrayAdapter<Pet> adapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_list_item_1, pets);showView.setAdapter(adapter);} catch (RemoteException e) {e.printStackTrace();}});}@Overrideprotected void onDestroy() {super.onDestroy();this.unbindService(conn);}
}
activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"android:layout_height="fill_parent"android:orientation="vertical"><Buttonandroid:id="@+id/get"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:text="@string/get" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@string/person" /><EditTextandroid:id="@+id/person"android:layout_width="fill_parent"android:layout_height="wrap_content"android:hint="@string/person"/><ListViewandroid:id="@+id/show"android:layout_width="fill_parent"android:layout_height="fill_parent"android:cacheColorHint="#00000000"android:textColor="#ffffff" />
LinearLayout>
注:当应用的版本是Android 11(API 级别 30)或更高版本时,需要在AndroidManifest.xml文件中添加,该标签的具体应用可见
<queries><package android:name="com.example.aidlparcelableservertest"/></queries>
或:
<queries><intent><action android:name="PARCELABLE_SERVICE"/></intent></queries>
- 运行程序:

2.4. 接口文件分析
接口中内容如下:
/** This file is auto-generated. DO NOT MODIFY.*/
package com.example.aidlparcelableservertest;public interface IPet extends android.os.IInterface {/*** Default implementation for IPet.*/public static class Default implements com.example.aidlparcelableservertest.IPet {// 定义一个Person对象作为传入参数@Overridepublic java.util.List<com.example.aidlparcelableservertest.Pet> getPets(com.example.aidlparcelableservertest.Person owner) 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.example.aidlparcelableservertest.IPet {// binder唯一标识// 不同的进程之间,通过序列化传递DESCRIPTOR来找到对应的Binder// 相同进程,也需要DESCRIPTOR才能找到对应的Binderprivate static final java.lang.String DESCRIPTOR = "com.example.aidlparcelableservertest.IPet";/*** Construct the stub at attach it to the interface.*/// 将interface提供出去,这样当同一进程其他位置执行IBinder.queryLocalInterface的时候就可以获取到这个Binderpublic Stub() {// 初始化时调用attachInterface(),相同进程调用queryLocalInterface()时才能找到该Binderthis.attachInterface(this, DESCRIPTOR);}/*** Cast an IBinder object into an com.example.aidlparcelableservertest.IPet interface,* generating a proxy if needed.*/// 接收服务端的IBinder(通常是传递给客户端onServiceConnected() 回调方法的参数),并返回Stub接口的实例。public static com.example.aidlparcelableservertest.IPet asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}// 调用attachInterface()方法,返回该Binderandroid.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);// server和client同一进程,直接通过queryLocalInterface(DESCRIPTOR)找到Binder返回Stub对象if (((iin != null) && (iin instanceof com.example.aidlparcelableservertest.IPet))) {return ((com.example.aidlparcelableservertest.IPet) iin);}// server和client不同进程,返回一个封装后的Proxy对象return new com.example.aidlparcelableservertest.IPet.Stub.Proxy(obj);}@Overridespublic android.os.IBinder asBinder() {// 返回stub的Binder对象return this;}// 服务端// 运行在服务器端中 Binder 线程池中,客户端发起跨进程请求时,远程请求会通过系统底层封装后交给此方法来处理// 方法调用由 onTransact() 代码分派,该代码通常基于接口中的方法索引。@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_getPets: {// 需要传递DESCRIPTOR,到另一个进程可以找到对应的Binderdata.enforceInterface(descriptor);com.example.aidlparcelableservertest.Person _arg0;if ((0 != data.readInt())) {// 读取CREATOR中参数_arg0 = com.example.aidlparcelableservertest.Person.CREATOR.createFromParcel(data);} else {_arg0 = null;}// 运行getPets()方法获取结果java.util.List<com.example.aidlparcelableservertest.Pet> _result = this.getPets(_arg0);reply.writeNoException();// 写入返回值reply.writeTypedList(_result);// 在执行完 return true 之后系统将会把 reply 流传回客户端,只是过程被隐藏了return true;}default: {return super.onTransact(code, data, reply, flags);}}}// 客户端private static class Proxy implements com.example.aidlparcelableservertest.IPet {private android.os.IBinder mRemote;Proxy(android.os.IBinder remote) {// Proxy在初始化时引用server的IBindermRemote = remote;}@Overridepublic android.os.IBinder asBinder() {// 返回当前Proxy的Binderreturn mRemote;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}// 定义一个Person对象作为传入参数@Overridepublic java.util.List<com.example.aidlparcelableservertest.Pet> getPets(com.example.aidlparcelableservertest.Person owner) throws android.os.RemoteException {// _data存储流向服务端的数据流android.os.Parcel _data = android.os.Parcel.obtain();// _reply存储流回客户端的数据流android.os.Parcel _reply = android.os.Parcel.obtain();java.util.List<com.example.aidlparcelableservertest.Pet> _result;try {// 需要传递DESCRIPTOR,到另一个进程可以找到对应的Binder_data.writeInterfaceToken(DESCRIPTOR);if ((owner != null)) {_data.writeInt(1);owner.writeToParcel(_data, 0);} else {_data.writeInt(0);}// 通过调用mRemote.transact()来触发远端Stub的onTransact() // 0:数据双向流通,1:数据从服务端流向客户端boolean _status = mRemote.transact(Stub.TRANSACTION_getPets, _data, _reply, 0);if (!_status && getDefaultImpl() != null) {return getDefaultImpl().getPets(owner);}_reply.readException();// 从_reply中取出服务端执行方法的结果_result = _reply.createTypedArrayList(com.example.aidlparcelableservertest.Pet.CREATOR);} finally {_reply.recycle();_data.recycle();}// 将结果返回return _result;}public static com.example.aidlparcelableservertest.IPet sDefaultImpl;}// getPets的调用id 调用IPC方法的唯一id// Stub.onTransact()中会通过TRANSACTION_getPets来对应执行getPets()方法的逻辑。// Proxy调用transact的时候,也是通过传递TRANSACTION_getPets,来标识自己想要执行的逻辑。static final int TRANSACTION_getPets = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);public static boolean setDefaultImpl(com.example.aidlparcelableservertest.IPet 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.example.aidlparcelableservertest.IPet getDefaultImpl() {return Stub.Proxy.sDefaultImpl;}}// 定义一个Person对象作为传入参数public java.util.List<com.example.aidlparcelableservertest.Pet> getPets(com.example.aidlparcelableservertest.Person owner) throws android.os.RemoteException;
}
DESCRIPTOR:Binder 中唯一的标识,自动生成时用当前 Binder 类名表示;TRANSACTION_getPets:声明的整型的 id 用于标识在 transact 过程中客户端中请求的到底是自身还是代理的getPets()方法;asInterface(android.os.IBinder obj) {...}:将服务端的 Binder 对象按是否同一进程转换成客户端所需 AIDL 接口类型的对象, 客户端和服务器在同一进程中,返回的是服务端 Stub 本身,否则就返回系统封装后的Stub.proxy对象;onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) {...}:运行在服务器端中 Binder 线程池中,客户端发起跨进程请求时,远程请求会通过系统底层封装后交给此方法来处理:通过 code 确定客户端的方法,再从 data 中取得参数 (如果存在参数的话),然后执行在服务端的目标方法,执行完成之后,向reply中写入返回值 (如果客户端中需要返回值的话);Proxy.getPets(com.example.aidlparcelableservertest.Person owner){...}:在客户端中运行,当客户端远程调用此方法时,内部实现方法如下:先创建所需的输入型 Parcel 对象_data, 输出型 Parcel 对象_replay和返回值对象_result。先将需求参数写入data 中,接着调用transact()方法发起远程调用 (RPC) 请求,同时当前线程会挂起,然后服务端的onTransact()方法会被调用,当 RPC 方法结束返回后,当前线程从挂起状态变成重新运行状态,并从reply中取出 RPC 过程的返回结果,最后返回_reply中的数据。
基本步骤如下:
- Client通过
ServiceConnection()获取到Server的Binder,并且封装成一个Proxy; - 通过Proxy来同步调用IPC方法。同时通过Parcel将参数传给Binder,最终触发Binder的
transact()方法; - Binder的
transact()方法最终会触发到Server上Stub的onTransact()方法; - Server上Stub的
onTransact()方法中,会先从Parcel中解析中参数,然后将参数带入真正的方法中执行,然后将结果写入Parcel后传回; - Client的IPC方法中,执行Binder的
transact()时,是阻塞等待的。一直到Server逻辑执行结束后才会继续执行; - 当Server返回结果后,Client从Parcel中取出返回值,于是实现了一次IPC调用。
bindService()->onBind()->onServiceConnected()->asInterface()->getPets()->transact()(Stub本身直接触发该方法,代理是在Proxy.getPets()方法中触发)->onTransact()->返回结果

3. ****标签解析
当创建的应用以 Android 11(API 级别 30)或更高版本为目标平台时,在默认情况下,系统只会让部分应用可见,而隐藏其他应用,以鼓励最小权限原则并保障用户的隐私安全。但是,应用的可见与否会影响到提供其他应用相关信息的方法的返回结果,如:queryIntentActivities()。此外,还会影响与其他应用的显式交互,例如启动另一个应用的服务。
3.1. 自动可见的应用
在 Android 11及以上版本,无需声明\便可进行交互的应用如下:
- 我们自己的应用;
- 实现 Android 核心功能的某些系统软件包;
- 安装了我们自己应用的应用;
- 使用
startActivityForResult()方法启动Activity的任何应用; - 启动或者绑定到我们自己的应用中的某项服务的任何应用;
- 访问我们自己的应用中的
ContentProvider的任何应用; - 具有
ContentProvider的任何应用,其中我们自己的应用已被授予URI权限来访问该ContentProvider;- 读取权限:
FLAG_GRANT_READ_URI_PERMISSION - 写入权限:
FLAG_GRANT_WRITE_URI_PERMISSION
- 读取权限:
- 我们自己的应用作为输入法应用提供输入,接收该输入的任何应用;
此外,无论某一应用对我们自己的应用是否可见,我们都可以使用隐式或者显示的intent来启动该应用的 activity。
如需查看特定设备的完整软件包列表,在该设备的终端中运行adb shell dumpsys package queries命令。在命令输出中,找到forceQueryable部分,即包含该设备上对我们自己的应用可见的软件包列表。如下图所示:

3.2. 查询应用
当创建的应用以 Android 11(API 级别 30)或更高版本为目标平台,并且需要与非自动可见的应用进行交互,则需要在该应用的AndroidManifest.xml清单文件中添加。在中,可以通过软件包名称package、intent签名或提供程序授权provide来查询特定软件包并与之交互。
3.2.1. 按软件包名称查询特定软件包及与之交互
当知道要查询或与之交互的一组特定应用时,可以将其软件包名称添加到内的一组元素中,如下所示:
<manifest package="com.example.game"><queries><package android:name="com.example.store" /><package android:name="com.example.services" /></queries>...
</manifest>
3.2.2. 按 intent 过滤器查询应用及与之交互
当需要查询一组具有特定用途的应用但却并不知道其具体的软件包名称时,可以在中按intent过滤器进行查询,使创建的应用能够查询到匹配的应用,如下所示:
<manifest package="com.example.game"><queries><intent><action android:name="android.intent.action.SEND" /><data android:mimeType="image/jpeg" /></intent></queries>...
</manifest>
元素的限制:
- 至少要有一个
或者一个元素,各属性至多只有一个; 中不能使用path、pathPrefix、pathPattern以及port属性,否则会被当作通用通配符*;中不能使用属性;- 在单个
的中,mimeType、scheme以及host最多使用一次,但可以在多个之间分配这些属性,也可以在单个中使用这些属性。
支持通用通配符*作为以下属性的值:
元素的name属性;元素的mimeType属性的子类型 (image/*);元素的mimeType属性的类型和子类型 (*/*);元素的scheme属性;元素的host属性。
除非前面列表中另有说明,否则系统不支持混合使用文本和通配符,如 prefix*。
3.2.3. 在给定提供程序授权的情况下查询应用及与之交互
当需要查询ContentProvider但不知道具体的软件包名时,可以在 元素中声明该提供程序的授权,如下所示:
<manifest package="com.example.suite.enterprise"><queries><provider android:authorities="com.example.settings.files" /></queries>...
</manifest>
可以在单个 元素中声明多项提供程序授权:
- 在单个
元素中,声明以英文分号分隔的授权列表; - 在同一个
元素中添加多个元素 ,在每个元素中,声明单项授权或以英文分号分隔的授权列表。
3.2.4. 查询所有应用及与之交互
在极少数情况下,我们创建的应用可能需要查询设备上的所有已安装的应用。此时,我们可以通过QUERY_ALL_PACKAGES权限以查询其他所有已安装应用,如下所示:
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
3.2.5. parseQueries方法解析
/*** 根据标签中的内容进行解析** @param input:与输入类型无关的,用来被转换为ParseResult输出类型的输入源参数* @param pkg:被解析的包* @param res:用于访问应用程序资源的类* @param parser:返回读取的XML资源* @return ParseResult:ParserInput的输出端*/
private static ParseResult<ParsingPackage> parseQueries(ParseInput input, ParsingPackage pkg,Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {final int depth = parser.getDepth();int type;// 判断是否还可以读取parser中的资源while ((type = parser.next()) != XmlPullParser.END_DOCUMENT&& (type != XmlPullParser.END_TAG || parser.getDepth() > depth)) {// 初始时刻 type = START_TAGif (type != XmlPullParser.START_TAG) {continue;}// 当queries中标签使用intent时if (parser.getName().equals("intent")) {// 对Intent中的action、category以及data信息进行解析,解析结果存放在result中ParseResult<ParsedIntentInfo> result = ParsedIntentInfoUtils.parseIntentInfo(null,pkg, res, parser, true /*allowGlobs*/, true /*allowAutoVerify*/, input);if (result.isError()) {return input.error(result);}// 获取含有解析的action、category以及data等信息的ParsedIntentInfo对象intentInfoParsedIntentInfo intentInfo = result.getResult();Uri data = null;String dataType = null;String host = null;// 分别获取action、data scheme(为空返回0)、data types(为空返回0)、Host的长度final int numActions = intentInfo.countActions();final int numSchemes = intentInfo.countDataSchemes();final int numTypes = intentInfo.countDataTypes();final int numHosts = intentInfo.getHosts().length;// 每个intent至少要有一个action或者一个data,各属性至多只有一个if ((numSchemes == 0 && numTypes == 0 && numActions == 0)) {return input.error("intent tags must contain either an action or data.");}if (numActions > 1) {return input.error("intent tag may have at most one action.");}if (numTypes > 1) {return input.error("intent tag may have at most one data type.");}if (numSchemes > 1) {return input.error("intent tag may have at most one data scheme.");}if (numHosts > 1) {return input.error("intent tag may have at most one data host.");}Intent intent = new Intent();// private ArrayList mCategories = null; // 将intentInfo获取的category属性添加到intent对象中for (int i = 0, max = intentInfo.countCategories(); i < max; i++) {intent.addCategory(intentInfo.getCategory(i));}// host属性赋值if (numHosts == 1) {host = intentInfo.getHosts()[0];}// data属性赋值if (numSchemes == 1) {data = new Uri.Builder().scheme(intentInfo.getDataScheme(0)).authority(host)// /*.path(IntentFilter.WILDCARD_PATH).build();}// dataType属性赋值if (numTypes == 1) {dataType = intentInfo.getDataType(0);// The dataType may have had the '/' removed for the dynamic mimeType feature.// If we detect that case, we add the * back.if (!dataType.contains("/")) {dataType = dataType + "/*";}// data设置默认值if (data == null) {data = new Uri.Builder().scheme("content")// */*.authority(IntentFilter.WILDCARD).path(IntentFilter.WILDCARD_PATH).build();}}intent.setDataAndType(data, dataType);if (numActions == 1) {intent.setAction(intentInfo.getAction(0));}// 将intent添加到空List queriesIntents中 pkg.addQueriesIntent(intent);} else if (parser.getName().equals("package")) { // 当queries中标签使用package时// 获取包名final TypedArray sa = res.obtainAttributes(parser,R.styleable.AndroidManifestQueriesPackage);final String packageName = sa.getNonConfigurationString(R.styleable.AndroidManifestQueriesPackage_name, 0);if (TextUtils.isEmpty(packageName)) {return input.error("Package name is missing from package tag.");}// 将packageName添加到空List queriesPackages中 pkg.addQueriesPackage(packageName.intern());} else if (parser.getName().equals("provider")) { // 当queries中标签使用provider时final TypedArray sa = res.obtainAttributes(parser,R.styleable.AndroidManifestQueriesProvider);try {// 获取authorities属性final String authorities = sa.getNonConfigurationString(R.styleable.AndroidManifestQueriesProvider_authorities, 0);if (TextUtils.isEmpty(authorities)) {return input.error(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,"Authority missing from provider tag.");}// 将多个authorities以";"拆分并加入到pkg中StringTokenizer authoritiesTokenizer = new StringTokenizer(authorities, ";");while (authoritiesTokenizer.hasMoreElements()) {// 将authorities添加到空Set queriesProviders中 pkg.addQueriesProvider(authoritiesTokenizer.nextToken());}} finally {sa.recycle();}}}return input.success(pkg);
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
