文章目录
如何在一个Android项目中使用多个进程
在AndroidManifest中配置使用android:process属性,因为这个属性只能在AndroidManifest中使用,所以跨进程只能作用于四大组件
这个属性的使用,有以下几种特点
- 如果没有配置 android:process这个属性,默认为项目的包名
- 这个属性可以配置 :remote 或者 包名.remote, 使用冒号这种方式,创建的是这个应用的私有进程,此进程不能与其他 应用 通过ShareId的方式跑在同一个进程中
- 因为使用android:process虽然新开了一个进程,但是还是之前的app,所以UID是一样的
可用 adb shell ps | grep 项目名 看PID和UID
USER PID PPID VSIZE RSS WCHAN PC NAME
u0_a145 10608 134 292960 26816 ffffffff 4019ca70 S com.ex.thre
u0_a146 10755 134 302420 32152 ffffffff 4019ca70 S com.ex.two
IPC(跨进程通信方式)
1. Bundle
通过Intent传递一个带有数据的Bundle来来实现跨进程通信,bundle的本质是一个parcelable对象
Intent intent = new Intent()
intent.putExtra()
2. 使用共享文件
比如A进程将数据存放在一个共有的目录下的文件中,然后让B进程去读,这样就实现了跨进程通信。但是两个进程共同去读写文件,会产生多进程安全问题,所以这种方式最好不要再并发量大的场景下使用。
还需要强调的一点是这个共享的文件不能是SharePrefernces文件,因为SP文件在内存中有一份缓存,所以多进程共同读写非常不可靠,容易造成数据丢失。
3.Messenger
Messenger封装了AIDL,可以让我们很容易的进行进程通信,但是使用Messenger一次只能处理一个请求即串行的处理,所以不用考虑多线程同步问题。
- 当不要求服务端响应时

- 如果需要服务端向客户端的回复一些数据
在这种场景下,需要让客户端也变为一个服务端,即在客户端用Handle新初始化一个Messenger用于处理服务端回复的数据。在发送请求时通过Message的replyTo字段将客户端这边的Messenger传递给服务端。

4. AIDL
AIDL服务端
第一步 创建AIDL文件
要想使用AIDL,首先需要创建AIDL文件,AIDL分为两种,一种是声明AIDAIDL服务端提供给AIDL客户端的接口,我们将它成为接口AIDL文件,另一种是声明接口AIDL文件中使用到的Parcelable对象,即Parcelable对象声明文件
具体的AIDL文件规范如下:
- 并不是所有的类型AIDL文件都支持,AIDL文件只支持如下几种类型
| 类型 | 是否需要显示import |
|---|---|
| 八大基本类型,比如int,float,long等 | 不需要 |
| String或者CharSequence | 不需要 |
| List,只支持ArrayList,要求List中的每一个元素都是AIDL文件支持的类型 | 不需要 |
| Map,只支持HashMap,要求Map中的每一个元素都是AIDL文件支持的类型 | 不需要 |
| 实现了Parcelable接口的对象 | 不但需要显示声明,还需要单独的AIDL文件使用parcelable关键字去声明这个对象,要求这个声明文件的全限定名和实际的Java类型保持一致 |
| AIDL接口 | 需要显示import |
- AIDL中除了基本数据类型和AIDL类型,一律需要在类型前声明 in, out, 或inout,不能一律的使用inout,因为这个在底层是由开销的
| 类型前声明 | 意义 |
|---|---|
| in | 表示数据只能由客户端流向服务端,服务端会获取到客户端完整的数据,但客户端不会同步服务端你对该对象的修改,不写的话,默认的 tag 就是 in |
| out | 表示数据只能由服务端流向客户端,从服务端端接受该对象不为空,但字段内容为空,服务端修改对象后,binder 远程调用返回后,客户端会收到修改后的对象 |
| inout | 则表示数据可在服务端与客户端之间双向流通 |
- AIDL接口中不像传统接口,可以定义静态场景,它只能定义方法
第二步 写AIDL服务
写一个服务将AIDL文件生成的binder对象, 返回给绑定这个服务的客户端
上一步写的AIDL文件会在编译阶段,自动帮助我们生成一个含有binder的类

自己创建一个Service类将该binder对象返回给客户端即可

AIDL客户端
通过ServiceConnection绑定AIDL服务获得binder对象即可调用AIDL服务端的接口

AIDL安全问题
如果创建了一个AIDL服务,任何进程都可以bind并并且调用AIDL接口是很不安全的,所以要对连接AIDL接口的客户端做限制
常用的显示方式有两种
-
自定义权限

可以在onbind方法进行这种权限判断 -
判断binder的身份
可以在AIDL文件生成的onTransact方法中通过getCallUid方法得知调用者的UID,再通过packageManager.getPackagesForUid()得到调用者的包名再加以判断
onway关键字
我们知道通过binder去跨进程通信,是会阻塞客户端当前线程的,如果在AIDL定义的接口中加了oneway关键字,那么该接口的调用将会变成异步的,不会阻塞当前线程。oneway关键字只适用于无返回值的接口
加了oneway关键字后AIDL文件与之前有什么不同

只是transact方法的flag从0 变为了 1
客户端给服务端注册回调无法注销
一般我们设置回调都会使用一个list去保存回调,注销时再将回调从list中删除,但是AIDL服务端收到的参数是反序列化过来的,所以注册的对象会想要注销的绝对不是一个对象,所以无法注销, 可用RemoteCallBackList解决这个问题

虽然不能以回调对象本身作为注销的依据,但是binder对象是同一个
还有就是虽然它看起来像个List但其实本质并不是,所以遍历时一定要注意要用以下的这种方式

监控AIDL服务端的消失
如果AIDL服务端被杀了,客户端有两种方式可以感应到
- 注册死亡通知
通过IBinder对象注册

当服务端死亡时, binderDied会在binder线程池中被回调 - ServerConnetion的回调

当服务端死亡时, serviceConnection会在onServiceDisconnected方法会在主线程中回调
binder连接池
binder连接池是一种思想,并不像线程池一样有封装好的类,需要自己实现
binder连接池可以解决一个AIDL服务端进程写了十几个Service的问题
大致思想如下图所示

什么是binder
是Android中的一个类,实现了IBinder接口,是Android中的一种跨进程通信的一种方式
下面我们通过AIDL来通知binder是工作过程
首先我们要创建三个文件
- Book.java ------- aidl请求接口中用到的parcelable对象
- IBookInterface.aidl ------- aidl请求接口
- Book.aidl ------- 声明Book.java对象

Book.java
package com.example.studyapplication;
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable {
private String name;
private int price;
protected Book(Parcel in) {
name = in.readString();
price = in.readInt();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(price);
}
}
IBookInterface.aidl
// IBooklInterface.aidl
package com.example.studyapplication;
// Declare any non-default types here with import statements
import com.example.studyapplication.Book;
interface IBooklInterface {
/**
* 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);
List<Book> getBookList();
void addBook(in Book book);
}
Book.aidl
// Book.aidl
package com.example.studyapplication;
// Declare any non-default types here with import statements
parcelable Book;
编译完程序后,你会发现在generated目录下生成了一个和aidl接口同名的java文件

该文件的分析如下
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.example.studyapplication;
public interface IBooklInterface extends android.os.IInterface {
/** Default implementation for IBooklInterface. */
public static class Default implements com.example.studyapplication.IBooklInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
{
}
@Override public java.util.List<com.example.studyapplication.Book> getBookList() throws android.os.RemoteException
{
return null;
}
@Override public void addBook(com.example.studyapplication.Book book) throws android.os.RemoteException
{
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.studyapplication.IBooklInterface {
//DESCRIPTOR(描述符)---- Binder的唯一标识, 一般为次binder接口的全限定名
private static final java.lang.String DESCRIPTOR = "com.example.studyapplication.IBooklInterface";
/** Construct the stub at attach it to the interface. */
public Stub() { this.attachInterface(this, DESCRIPTOR); }
/**
* Cast an IBinder object into an com.example.studyapplication.IBooklInterface interface,
* generating a proxy if needed.
* 将服务端的binder对象强制转换为客户端的AIDL接口对象
* 如果客户端和服务端在不同进程,会生成代理,反之不会
*/
public static com.example.studyapplication.IBooklInterface asInterface(android.os.IBinder obj) {
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.studyapplication.IBooklInterface))) {
return ((com.example.studyapplication.IBooklInterface)iin);
}
return new com.example.studyapplication.IBooklInterface.Stub.Proxy(obj);
}
//返回当前的binder对象
@Override public android.os.IBinder asBinder() { return this; }
/*
这个方法运行在服务端的线程池中,code是方法Id,AidL接口定义的每个方法都有一个id,data是要调用的方法的参数,如果要调用的方法有返回值会写入reply中
如果客户端请求失败onTransact方法会返回false
*/
@Override public 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_getBookList:
{
data.enforceInterface(descriptor);
java.util.List<com.example.studyapplication.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(descriptor);
com.example.studyapplication.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.example.studyapplication.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.example.studyapplication.IBooklInterface {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {mRemote = remote;}
@Override public 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.
*/
@Override public 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();
}
}
/*
这个方法运行在客户端中,大致的调用逻辑为:
创建两个Parcel对象_data,和_reply,_负责写入请求方法的参数,_reply负责写入该方法的返回值
紧接着RPC调用服务端的transact方法,此时此方法的调用进程挂起,等待服务端的返回结果
*/
@Override public java.util.List<com.example.studyapplication.Book> getBookList() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.studyapplication.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getBookList();
}
_reply.readException();
_result = _reply.createTypedArrayList(com.example.studyapplication.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/*
此方法的调用原理和上面的一致
*/
@Override public void addBook(com.example.studyapplication.Book book) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book!=null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
boolean _status = mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addBook(book);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public static com.example.studyapplication.IBooklInterface sDefaultImpl;
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
//getBookList方法的ID
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
//addBook方法的ID
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
public static boolean setDefaultImpl(com.example.studyapplication.IBooklInterface 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.studyapplication.IBooklInterface 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.util.List<com.example.studyapplication.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.example.studyapplication.Book book) throws android.os.RemoteException;
}
将上面的流程话为流程图

由于client调用aidl请求接口会挂起当前线程,所以不建议在主线程中调用
其实我们发现,aidl文件的作用仅仅是帮助我们自动生成binder对象,其实也可以我们自己实现。
本文详细介绍了Android中常见的四种IPC通信方式:Bundle、共享文件、Messenger和AIDL,重点解析了AIDL的使用步骤、安全问题以及注意事项。通过AIDL服务端和客户端的示例,展示了如何实现跨进程通信。同时,提到了binder连接池的概念,用于优化多个Service的管理。
1868

被折叠的 条评论
为什么被折叠?



