Java Binder 之 IBinder

1. IBinder

IBinder是远端对象的基本接口,是轻量级远端过程调用(RPC)机制的核心部分,旨在在进程内和跨进程调用时实现高性能。该接口描述了与远端对象交互的抽象协议。应从Binder扩展该接口,而不能直接实现该接口。

IBinder最关键的API是transact()和与之相匹配的Binder.onTransact()。它们可以向IBinder对象发送调用并接收对Binder对象的调用。该API是同步的,因此在Binder.onTransact()返回之前,transact()的调用不会返回。这是调用本地进程存在的对象时的预期行为,底层的进程间通信(IPC)机制可确保跨进程时应用这些语义。

通过transact()发送的数据是Parcel类型的,Parcel通用数据缓冲区,还维护一些有关其内容的元数据。元数据用于管理缓冲区中的IBinder对象引用,以便在缓冲区跨进程移动时可以维持这些引用。这种机制确保了,当将IBinder写到Parcel中并发送到另一个进程时,若另一个进程将对同一个IBinder的引用发送给原进程,则原进程将返回同一个IBinder对象。这些语义允许将IBinder/Binder对象用作可以跨进程管理的唯一标识(用作token或其它目的)。

系统在运行的每个进程中维护一个事务线程池。这些线程用于分发来自其他进程的所有IPC。例如,当从进程A到进程B创建IPC时,A中的调用线程在将事务发送到进程B时在transact()中阻塞。B中的下一个可用池线程接收到传入的事务,在目标对象上调用Binder.onTransact(),并返回结果Parcel。接收到其结果后,进程A中的线程将返回以允许其继续执行。

Binder系统还支持跨进程的递归。例如,如果进程A执行到进程B的事务,并且进程B在处理该事务时对在A中实现的IBinder上的调用transact()进行了处理,那么当前正在等待原事务完成的进程A中的线程将非常关注进程B调用对象调用Binder.onTransact()的过程。这确保了调用远端binder对象时的递归语义与调用本地对象时的递归语义相同。

在使用远端对象时,可以通过下面三种方法确定它们何时不再有效:

(1)如果尝试在已不存在的进程的IBinder上调用transact()方法,则会抛出RemoteException异常。

(2)可以调用pingBinder()方法,如果远程进程已不存在,则将返回false。

(3)linkToDeath()方法可用于向IBinder注册DeathRecipient,当进程挂掉时会调用该方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// frameworks/base/core/java/android/os/IBinder.java
public interface IBinder {
// 用于user命令的第一个事务代码1。
int FIRST_CALL_TRANSACTION = 0x00000001;
// 用于user命令的最后一个事务代码16777215。
int LAST_CALL_TRANSACTION = 0x00ffffff;
// IBinder协议事务代码:pingBIinder()。
int PING_TRANSACTION = ('_'<<24)|('P'<<16)|('N'<<8)|'G';
// IBinder协议事务代码:dump内部状态。
int DUMP_TRANSACTION = ('_'<<24)|('D'<<16)|('M'<<8)|'P';
// IBinder协议事务代码:执行shell命令。
int SHELL_COMMAND_TRANSACTION = ('_'<<24)|('C'<<16)|('M'<<8)|'D';
// IBinder协议事务代码:向事务的接收方询问规范的接口描述符。
int INTERFACE_TRANSACTION = ('_'<<24)|('N'<<16)|('T'<<8)|'F';
// IBinder协议事务代码:向目标对象发送推文。Parcel中的数据旨在传递给与对象关联的共享消息服务;
// 可将其存在在普通消息传递服务中,它可以是130个UTF-8字符,也可以是任何字符。
// 作为HONEYCOMB_MR2版本的一部分,所有Binder对象都应支持此协议,以便在整个平台上进行完全集成的推文。
// 为了支持较旧的代码,默认将推文记录到主日志中,作为在网络上公开广播该推文的简单模拟。
// 在完成分发后,对象必须冲泡一杯茶,将其退还给呼叫者。
int TWEET_TRANSACTION = ('_'<<24)|('T'<<16)|('W'<<8)|'T';
// IBinder协议事务代码:异步告诉应用程序调用者like它。该应用负责增加和维护自己的类似计数器,并且可以向用户显示该值以标识该应用的质量。
// 这是应用程序不需要处理的可选命令,因此默认实现是不执行任何操作。
// 无返回值,有关该系统的任何信息都不会受到功能的影响,但是它将改善应用程序。
int LIKE_TRANSACTION = ('_'<<24)|('L'<<16)|('I'<<8)|'K';
@UnsupportedAppUsage
int SYSPROPS_TRANSACTION = ('_'<<24)|('S'<<16)|('P'<<8)|'R';
// transact的flag:这是单向调用,这意味着调用者无需等待被呼叫者的结果而立即返回。仅在调用者和被调用者处于不同进程时适用。
// 系统为对同一IBinder对象的多个单向调用提供了特殊的排序语义:这些调用将在另一个进程中分发一次,其顺序与原始调用相同。
// 这些仍然由IPC线程池分发,因此可以在不同的线程上执行,但是直到前一个线程完成后才分发下一个线程。
// 对于在不同IBinder对象上的调用,或在同一IBinder对象上混合单向和非单向调用时,不能保证此顺序。
int FLAG_ONEWAY = 0x00000001;
// 设置IPC最大大小,以使它们安全地保持在事务缓冲区限制之内。
public static final int MAX_IPC_SIZE = 64 * 1024;
// 获取此binder程序支持的接口的规范名称。
public @Nullable String getInterfaceDescriptor() throws RemoteException;
// 检查对象是否存在,如果宿主进程已死则返回false。
public boolean pingBinder();
// 检查binder所在的进程是否一直存活alive。
// 如果进程死亡则返回false。需要注意的是,如果返回true,则该进程也有可能在调用返回时死亡。
public boolean isBinderAlive();
// 尝试为此Binder对象查询接口的本地实现。如果返回null,则需要实例化代理类以通过transact()方法封装调用。
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor);
// 将对象的状态打印到给定的流中。
// 第一个参数fd:dump发送到的原始文件描述符。
// 第二个参数args:dump需要的其他参数。
public void dump(@NonNull FileDescriptor fd, @Nullable String[] args) throws RemoteException;
// 像dump(FileDescriptor, String[]),但总是异步执行。如果对象是本地对象,则会创建一个新线程来执行dump。
public void dumpAsync(@NonNull FileDescriptor fd, @Nullable String[] args)
throws RemoteException;
// 在这个对象上执行一个shell命令。这可以从调用者异步执行; 完成时,实现必须始终调用resultReceiver。
// 第一个参数in:可以从中读取输入数据流的原始文件描述符。
// 第二个参数out:普通命令消息应写入的原始文件描述符。
// 第三个参数err:应该将命令错误消息写入的原始文件描述符。
// 第四个参数args:命令行参数。
// 第五个参数shellCallback:调用者的shell的可选回调,以在其中执行操作。
// 第六个参数restultReceiver:当命令执行完成时调用,会由结果码。
public void shellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
@Nullable FileDescriptor err,
@NonNull String[] args, @Nullable ShellCallback shellCallback,
@NonNull ResultReceiver resultReceiver) throws RemoteException;
// 对对象执行一般操作。
// 第一个参数code:要执行的动作。这应该是FIRST_CALL_TRANSACTION和LAST_CALL_TRANSACTION之间的数字。
// 第二个参数data:发送到目标的编组后的数据,不能为null。如果不发送任何数据,则必须创建一个在此处指定的空parcel。
// 第三个参数reply:从目标接收到的编组数据。返回值可以为null。
// 第四个参数flags:附加操作标志。对于普通RPC,请选择0;对于单向RPC,请使用FLAG_ONEWAY。
// 第五个参数return:从onTransact返回结果。成功调用通常返回true;否则,返回true。false通常表示不了解事务代码。
public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)
throws RemoteException;
// 当宿主IBinder挂掉后用于接收回调的接口。
public interface DeathRecipient {
public void binderDied();
}
// 如果挂掉了需要为接收者注册通知。如果此binder对象意外挂掉(通常是因为其宿主进程已被杀死),则将调用给定的DeathRecipient.binderDied()方法。
// 将仅收到远程binder的死亡通知,因为根据定义,本地binder也不会死。
// 如果目标IBinder的进程已经死亡,则抛出RemoteException。
public void linkToDeath(@NonNull DeathRecipient recipient, int flags)
throws RemoteException;
// 删除之前注册的死亡通知。如果该对象死亡,将不再调用接收者。
// 若recipient已成功取消链接,则确保不会调用其DeathRecipient.binderDied()方法;如果目标IBinder已经死亡,则意味着该方法已被(或即将)被调用。
// 若给定的recipient尚未在IBinder中注册,但是IBinder已经死亡,那么将抛出not异常,并且将收到一个错误的返回值。
public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags);
}