IPC 即 Inter-Process Communication 进程间通信,Android 使用多进程的常用方法是 android:process 属性,属性值有两种形式,分别是 :process_name 和 process_name,前者的 :
表示在前插入包名,后者则是全名。
进程是独立完整的概念,不同进程分别部署于独立的虚拟机中,static 静态全局变量中的全局表示的就是整个进程,所以多进程模式的应用程序会造成很多问题
- 静态成员和单例模式完全失效
- 线程同步机制完全失效
- SharedPreferences 的可靠性下降
- Application 多次重建
序列化
Serializable 接口
Java 提供的序列化接口,是没有方法的标记接口,只是告诉 JVM 可以序列化实现 Serializable 接口的类,使用方法简单,利用序列化流的 writeObject 和 readObject 方法进行序列化,系统的自动化处理依赖于反射,所以性能略低。
Serializable 实现类可以使用 serialVersionUID 来校验反序列化正确性,没有指定 serialVersionUID 的序列化实现类在字段增删或构造方法调整时其 serialVersionUID 会变化,旧实例流无法反序列化成调整后的类,解决方法是在实现类中指定 serialVersionUID 的值,保证应用程序不同版本调整后数据可以兼容反序列化。
Parcelable 接口
Android 提供的序列化接口,不依赖反射,性能更高,通过轻量化的 Parcel 内存容器将对象打包,广泛用于 IPC 跨进程通信中,写入和读取 Parcel 都是顺序操作。
java
public class User implements Parcelable {
private String id;
private int level;
public User(String id, int level) {
this.id = id;
this.level = level;
}
protected User(Parcel in) {
id = in.readString();
level = in.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeiInt(level);
} // flags = PARCELABLE_WRITE_RETURN_VALUE 表示对象即将返回给 Binder
@Override
public int describeContents() { // 是否包含特殊类型的文件描述符
return 0; // 如果包含则返回 CONTENTS_FILE_DESCRIPTOR
}
public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
}
}
Parcelable 对 Serializable 的优化类似 Dagger2 的迭代提升,抛弃运行时反射可能造成的应用响应缓慢,增加代码复杂度和编译时长来换取应用流畅运行。
Binder
Binder 是 Android 的 IBinder 接口的实现类,是 Android 核心的跨进程通信方式,也是虚拟的驱动设备,设备驱动在 /dev/binder。从 Framework 角度 Binder 是 ServiceManager 连接各挂载 Server 服务端进程的桥梁;从 Application 角度 Binder 是 Server 服务端和 Client 客户端进行通信的媒介。
AIDL
Android Interface Definition Language 安卓接口定义语言,用来解决 Android 的跨进程通信问题,底层原理是 Binder,通过 C/S 架构来辅助开发者实现跨进程通信。使用 AIDL 实现跨进程通信要提供实现 Parcelable 的数据类、声明数据类的 aidl 文件和向客户端暴露接口的 aidl 文件。
java
// User.aidl
package com.example.aidl
parcelable User;
// IUserManager.aidl
package com.example.aidl;
import com.example.aidl.User;
interface IUserManager {
String getId();
int getLevel();
}
Build 得到的 IUserManager 类继承自 AIDL 的根接口 IInterface,根接口有唯一的抽象方法 asBinder 用于返回这个接口对应的 Binder 对象。在 IUserManager 内部有继承自抽象类 Binder ( android.os.Binder ) 、实现 IUserManager 的 Stub 类,Stub 表示服务端桩,有很多字段和实现方法
- DESCRIPTOR:服务端 Binder 的唯一标识
- asInterface 方法:返回客户端所需的 aidl 接口的实现类对象(Stub 或 Proxy)
- onTransact 方法:接收来自客户端的 Parcel,解包并根据参数执行客户端本地方法,把执行结果再写入 Parcel,onTransact 属于回调方法
Stub 内部存在 IInterface 的静态实现类 Proxy,用于在客户端进程把本地方法转换成跨进程的 Binder 事务调用,属于代理对象,将调用方法的参数写入 Parcel,Proxy 内部包含 IBinder 类型的 mRemote 字段,它是 BinderDriver 分配的代理对象句柄,Proxy 通过调用 mRemote.transact() 将 Binder 事务发送给 BinderDriver,BinderDriver 会回调对应服务端 Stub 的 onTransact 执行并返回结果。
Question
Binder 体系中的主要成员有哪些
- 获取 Binder 引用(Proxy 的 mRemote)的 Client 客户端
- 持有 Binder 对象,对外暴露接口的 Server 服务端
- 管理不同服务端 Binder 实例的 ServiceManager ServiceManager 本身是系统进程,所以客户端获取 Proxy 和通过 Proxy 调用远程方法属于同类的 Binder 事务。ServiceManager 持有注册名 -> 客户端 Binder 的注册表。
- 底层驱动 BinderDriver BinderDriver 持有 Binder 代理对象句柄 -> 内存 位置的引用表。

-
IBinder、Binder 和 IInterface 有什么关系
Binder 本身是 IBinder 接口的抽象实现类,IInterface 是 AIDL 的根业务接口层,开发者自拟的 aidl 文件会编译成 IInterface 的实现类,其包含 Stub、Proxy 和自拟的方法签名,Stub 继承自 Binder,Proxy 持有服务端 Binder 引用。
BinderDriver 是怎么在 Proxy.mRemote.transact() 的 Binder 事务中发挥作用的
transact 方法最终会执行到应用层的 transactNative 方法,通过 JNI 进入 Native 层的 android_util_Binder.cpp,在 Native 层最终调用 IPCThreadState::self()->transact(),接下来通过 ioctl 系统调用,把事务数据写入 BinderDriver 的 /dev/binder 设备。
写入后 BinderDriver 开始正式接手,此时事务执行到 Linux 内核层,内核模块的 drivers/android/binder.c 首先存有 Binder 对象的引用映射表,BinderDriver 会按引用表找目标 Binder 进程,将事务加入该进程的等待队列,并唤醒正在等待 Binder 的线程,回调触发客户端的 onTransact。
mRemote 的具体引用链是怎样的
传入 mRemote 的 IBinder 是应用层的系统类实例 android.os.BinderProxy,它是真正的 IBinder 实现,内部通过 JNI 和 Native 层的 BpBinder 对接,持有 Binder 驱动分配的代理对象句柄 handle。
客户端希望调用远程方法,但是远程对象在服务端进程的内存中,不能直接拿指针,所以 Binder 驱动分配给客户端一个整数编号,这个整数编号就是代理对象句柄 handle,表示客户端持有的服务端 Binder 的引用,在 BinderDriver 通过 handle 查找对应服务端 Binder 进程的内存位置。
Binder 死亡代理
Binder 运行在服务端进程,如果服务端进程意外终止会导致 Binder 链接断裂,称为 Binder 死亡,为了解决 Binder 死亡时客户端功能异常的问题,Binder 有 2 个配对方法 linkToDeath 和 unlinkToDeath,通过前者可以给 Binder 设置死亡代理,Binder 死亡后会触发死亡代理的回调方法来重新发起连接请求。
java
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (iUserManager == null) { return; }
iUserManager.asBinder().unlinkToDeath(this, 0);
iUserManager = null;
}
}
// 客户端绑定远程服务成功后
mService = IUserManager.Stub.asInterface(binder);
binder.linkToDeath(mDeathRecipient, 0);
死亡代理定义和绑定的操作都应写在客户端类中,其中 iUserManager 是远程接口代理对象,即 IUserManager.Stub.asInterface,绑定操作写在 onServiceConnected 回调里,binder 即远程传来的 Binder 对象,将 binder 转成 AIDL 接口,再对 binder 注册死亡代理。
Android 中的 IPC 方式
Intent
Intent 可以跨进程启动组件,本质是 IPC 机制和 AMS 的封装,当我们调用 startActivity 方法,其调用链最终会执行到 Instrumentation.execStartActivity 方法,该方法会通过 ActivityTaskManager.getService 获取系统 ActivityTaskManagerService 的 Binder 代理 mActivityTaskManager 并使代理调用 startActivity 方法,相当于远程调用系统进程的方法,此时已经发生了 Binder IPC 调用,调用进程 -> system_server 进程。
system_server AMS 进程接收到启动请求后首先会检查目标组件的权限和 IntentFilter 匹配属性,以及目标组件进程是否存在,不存在则调用 ActivityManagerService.startProcessLocked 方法创建,接下来找到目标线程后会构造启动参数,通过 Binder 调用目标进程的 ApplicationThread,目标进程接收到消息后开始创建组件。
文件共享
文件共享不相关 Binder 的 IPC 机制,只需要双方持有目标文件的读写权限即可,Android 基于 Linux 系统,没有对并发读写文件做出限制,所以文件共享的方式适合在数据同步要求不高的进程之间进行通信。
Messenger
Messenger 可以在不同进程传递 Message 对象,是轻量级的 IPC 方案,通过构造方法可以知道其底层实现是 AIDL
java
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
首先我们在服务端创建 Service 来处理客户端的连接请求,创建 Handler 实例并通过它创建 Messenger 对象,在 Service 的 onBind 方法通过返回该 Messenger 对象的底层 Binder。接下来在客户端进程绑定服务端 Service 后,通过服务端返回的 IBinder 对象创建 Messenger,通过该对象就可以向服务端发送 Message 了,如果希望服务器能够回应客户端,要借助 Handler 创建新的 Messenger 对象,把这个对象通过 Message 的 replyTo 参数传递给服务端,服务端通过该 replyTo 参数即可回应客户端。
注意到 Messenger 实现服务端回复功能的前提是 Message 数据携带 Messenger 实例,Messenger 是 Binder 的简易封装,本质是驱动句柄引用,它在 Binder 层面是安全的,但只是单个 Binder 通道的 1 次引用传递,客户端随时都可能退出,而 Message 可能处于传输阶段,所以生命周期管理薄弱,也因为 Messenger 的消息处理通过 Handler 的 handleMessage,底层是单线程串行处理的,多客户端场景弱,所以 Messenger 是简易临时的 IPC 桥接方案。
java
// Constants.java
public final class Constants {
public static final int MSG_FROM_CLIENT = 1;
public static final int MSG_FROM_SERVICE = 2;
private Constants() {}
}
// Service:android:process=":remote"
public class MessengerService extends Service {
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case Constants.MSG_FROM_CLIENT:
Log.d("MessengerService", "收到客户端消息: " + msg.getData().getString("msg"));
Messenger client = msg.replyTo;
Message replyMessage = Message.obtain(null, Constants.MSG_FROM_SERVICE);
Bundle bundle = new Bundle();
bundle.putString("reply", "Service已收到: " + receivedMsg);
replyMessage.setData(bundle);
try {
client.send(replyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
private final Messenger mMessenger = new Messenger(new MessengerHandler());
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
// Client
public class MessengerActivity extends AppCompatActivity {
private Messenger mService;
private boolean mBound;
private final Messenger mReceiverMessenger = new Messenger(new IncomingHandler());
private static class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case Constants.MSG_FROM_SERVICE:
String reply = msg.getData().getString("reply");
Log.d("MessengerActivity", "收到Service回复: " + reply);
break;
default:
super.handleMessage(msg);
}
}
}
private final ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
mBound = true;
Message msg = Message.obtain(null, Constants.MSG_FROM_CLIENT);
Bundle data = new Bundle();
data.putString("msg", "你好,这里是客户端。");
msg.setData(data);
msg.replyTo = mReceiverMessenger;
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName className) {
mService = null;
mBound = false;
}
};
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, MessengerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
}
AIDL
前面提到 AIDL 自动生成代码的结构,那么如何使用 AIDL 实现 IPC 呢,仍旧分为服务端和客户端看:服务端层面,首先要创建 Service 监听客户端的连接请求,创建 AIDL 文件并在该文件声明暴露给客户端的方法接口,最后在 Service 实现该 AIDL 接口;客户端首先要绑定服务端的 Service,绑定成功后将服务端返回的 Binder 对象转换成 AIDL 接口所需的类型,接下来就可以调用 AIDL 的方法了。
java
// Book.aidl
package com.example.aidl;
parcelable Book;
// ---
// IBookManager.aidl
package com.example.aidl
import com.example.aidl.Book;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
// ---
// BookManagerService.java (android:process=":remote")
public class BookManagerService extends Service {
private static final String TAG = "BookManagerService";
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
private Binder mBinder = new IBookManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
};
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1, "Android第一行代码"));
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
// ---
// BookManagerActivity.java
public class BookManagerActivity extends Activity {
private static final String TAG = "BookManagerActivity";
private ServiceConncetion mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
IBookManager bookManager = IBookManager.Stub.asInterface(service);
try {
List<Book> list = bookManager.getBookList();
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void onServiceDisconnected(ComponentName className) {}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book_manager);
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestory() {
unbindService(mConncetion);
super.onDestory();
}
}
ContentProvider 和 Socket 也可以作为 IPC 方式,因为篇幅过长,我们将其连同 Binder 连接池作为后日谈择日补充。