本文分析基于Android S(12)
前言
当我们使用AIDL接口时,拿到的对象本质上属于Stub.Proxy
类。通过Binder通信,便可以将数据传输给Server进程中的Stub对象(继承于Binder类)。然而这些只是冰山一角,在水面以下还隐藏着许多其他对象。有了它们,通信才能够建立。我们以ApplicationThread
为例,通信发起方拿到的是IApplicationThread.Stub.Proxy
对象,数据最终到达的是ApplicationThread
对象(ApplicationThread继承于IApplicationThread.Stub类)。下图展示了整个通信链路建立时需要创建的不同对象,总共有9个(其中的细节可以参考本人先前的文章《Binder代理机制上》)。
图中的序号表示的是这些对象的创建顺序,第一个出现的是Stub对象(也可称为Binder对象,因为它的父类是Binder,根据多态原则我们可以将它转为Binder对象)。Binder对象构造时会顺带创建Native层的JavaBBinderHolder对象,这样我们便拥有了1、2两个对象。而后续3~9这些对象只有当我们将Binder对象作为数据传输给对端进程后才能创建出来。通信链路建立以后,即便Binder对象在本进程内没有被任何其他Java对象引用,它也不应该被回收,因为它随时会收到来自其他进程的通信数据。这种关系可以被形容为:Server进程的Binder对象被Client进程的BinderProxy对象引用了。请记住这句话,它是本文思想的核心。只有当其他进程中对应的Proxy对象都被GC回收以后,Binder对象不再可能接收新的通信数据时,它才可以被回收,如下图所示。
为了建立这种跨进程的引用关系,我们需要一些复杂的过程和中间变量的配合。一旦了解了这种引用关系的内部细节,我们便可以知道这些对象的生命周期是如何管理的。理解生命周期,其实核心回答的就是两个问题:
- Binder对象如何保证在BinderProxy对象存在时不被回收?
- Binder对象如何在所有BinderProxy消亡时被回收?
带着这些问题,接下来我们将要深入细节,看看Android到底是如何管理这些对象的生命周期的。
1. Server进程
1.1 Binder对象
整个过程始于Binder对象的创建。
public Binder(@Nullable String descriptor) {
mObject = getNativeBBinderHolder();
NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mObject);
mDescriptor = descriptor;
}
Binder的构造方法中做了三件事:
getNativeBBinderHolder
在Native层创建了一个新的JavaBBinderHolder
对象,并将它的地址返回给mObject字段。registerNativeAllocation
用于管理与Java对象有关的native内存,具体可参考本人文章《如何让GC同步回收native内存》。- 将AIDL接口名称记录在mDescriptor中。
此时创建的Binder对象还只是一个普通的Java对象,如果不将它作为Binder数据(Binder通信既可以传输普通数据,也可以传输Binder类实例化后的对象)传输给其他进程,那么后续的JavaBBinder对象、binder_node对象都不会创建,它也就不会具备跨进程通信的能力。
通过new方式创建出来的JavaBBinderHolder对象并没有通过强弱指针(智能指针)进行管理,而是将地址寄存在Binder对象的字段中,因此它的回收也只能由Binder对象发起。
根据NativeAllocationRegistry机制可知,Binder对象被GC回收时会调用Binder_destroy
函数,这样JavaBBinderHolder对象也就被销毁了。
static void Binder_destroy(void* rawJbh)
{
JavaBBinderHolder* jbh = (JavaBBinderHolder*) rawJbh;
ALOGV("Java Binder: deleting holder %p", jbh);
delete jbh;
}
1.2 JavaBBinder对象
当我们将Binder对象作为数据传输给对端进程时,首先需要将它装入Parcel对象,这就好比数据打包。Java层的writeStrongBinder方法会进入到Native层,其中会有两个关键的步骤,一个是ibinderForJavaObject
函数,另一个是parcel->writeStrongBinder
函数。
static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object)); //object即为Binder对象
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
}
}
ibinderForJavaObject
函数会根据Java对象生成Native层的JavaBBinder对象。在构造JavaBBinder对象时,我们会通过env->NewGlobalRef
的方式将Binder对象加入到art::globals_列表中,这样Binder对象在每次GC时都会被标记为GC Root,也便无法被回收。通过这种方式,我们将Java层的Binder对象和Native层的JavaBBinder对象进行了深度捆绑,只有当JavaBBinder对象销毁时,Binder对象才能从art::globals_中清除,重新恢复自由身后他就可以被回收了。
JavaBBinder(JNIEnv* env, jobject /* Java Binder */ object)
: mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object))
{
ALOGV("Creating JavaBBinder %p\n", this);
gNumLocalRefsCreated.fetch_add(1, std::memory_order_relaxed);
gcIfManyNewRefs(env);
}
ibinderForJavaObject
函数的返回值为sp<IBinder>类型。这里的sp表示"strong pointer",它是Android中单独实现的一套智能指针系统,切勿和C++中的shared_ptr混淆。sp是一个模板类,其中作为模板的类必须继承自RefBase,比如这里的IBinder就继承了RefBase。sp通过引用计数的方式来管理所指向的C++对象,这里的话是管理新创建的JavaBBinder对象。关于RefBase的详细知识不是本文的重点,因此这里介绍一个参考文章,供没有背景知识的同学参考。
sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
{
...
// Instance of Binder?
if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
JavaBBinderHolder* jbh = (JavaBBinderHolder*)
env->GetLongField(obj, gBinderOffsets.mObject);
return jbh->get(env, obj);
}
...
}
sp<JavaBBinder> get(JNIEnv* env, jobject obj)
{
AutoMutex _l(mLock);
sp<JavaBBinder> b = mBinder.promote();
if (b == NULL) {
b = new JavaBBinder(env, obj); //b被赋值时,JavaBBinder对象的强引用计数+1,此时该值为1
...
}
return b;
}
当JavaBBinder对象的强引用计数减为0时,JavaBBinder便会被销毁,这就是Android中智能指针的基本逻辑。sp对象的创建会增加JavaBBinder的强引用计数(+1),sp对象的销毁会减少JavaBBinder的强引用计数(-1)。因此上述get函数中JavaBBinder对象的强引用计数值呈现出如下规律:
b = new JavaBBinder(env, obj)
会对JavaBBinder对象的强引用计数值+1,于是该值为1。return b
会根据局部变量b创建一个sp类型的返回值,这里采用的是拷贝构造,因此JavaBBinder的强引用计数值+1,于是该值为2。- 函数返回,局部变量b销毁,JavaBBinder的强引用计数值-1,于是该值为1。
ibinderForJavaObject
返回后,返回值将作为参数继续进入到parcel->writeStrongBinder
函数。
status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
return flattenBinder(val);
}
status_t Parcel::flattenBinder(const sp<IBinder>& binder)
{
...
flat_binder_object obj;
if (binder != nullptr) {
BBinder *local = binder->localBinder();
if (!local) {
...
} else {
...
obj.hdr.type = BINDER_TYPE_BINDER;
obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
obj.cookie = reinterpret_cast<uintptr_t>(local);
}
}
...
status_t status = writeObject(obj, false);
return finishFlattenBinder(binder);
}
flattenBinder
会根据JavaBBinder对象构造一个flat_binder_object对象,并将其写入Parcel。而作为参数的sp对象binder
在函数返回后将不复存在,因此指向的JavaBBinder对象强引用计数值将会继续减1。根据上文可知,此时JavaBBinder对象的强引用计数值为1,如果flattenBinder里没有其他地方增加JavaBBinder对象的强引用计数值,那么函数返回后,强引用计数值归零,JavaBBinder对象将会被销毁。而这显然是不符合预期的。
等等,让我们站在宏观的角度来重新思考这个问题。
当我们将构造后的flat_binder_object对象写入Parcel,意味着该Parcel对象隐式地引用了JavaBBinder对象,因此它需要保证自己在使用flat_binder_object对象的过程中,JavaBBinder对象不被销毁。基于这个逻辑的判断,flattenBinder
中调用writeObject
将flat_binder_object对象写入Parcel的过程里一定会增加JavaBBinder的强引用计数。下面我们来看看代码的设计是否符合我们逻辑的推断。
status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData)
{
...
// Need to write meta-data?
if (nullMetaData || val.binder != 0) {
mObjects[mObjectsSize] = mDataPos;
acquire_object(ProcessState::self(), val, this, &mOpenAshmemSize);
mObjectsSize++;
}
...
}
static void acquire_object(const sp<ProcessState>& proc,
const flat_binder_object& obj, const void* who, size_t* outAshmemSize)
{
switch (obj.hdr.type) {
case BINDER_TYPE_BINDER:
if (obj.binder) {
LOG_REFS("Parcel %p acquiring reference on local %p", who, obj.cookie);
reinterpret_cast<IBinder*>(obj.cookie)->incStrong(who);
}
return;
...
}
果然,当传输的是JavaBBinder对象时,writeObject
会调用acquire_object
来增加JavaBBinder对象的强引用计数(通过incStrong函数)。这样一来,flattenBinder函数返回后JavaBBinder对象也就不会被销毁了。
那么acquire_object
里增加的强引用计数何时再减掉呢?答案自然是Parcel对象回收的时候。事实上,Parcel对象在通信链路的建立过程中只是二传手的角色,它只能保证JavaBBinder一直存活到对端进程创建出BinderProxy对象的时候,而后续JavaBBinder的生命就交给了对端进程。
下面我们来看一个典型的AIDL gnerated方法。
@Override
public void registerRemoteAnimations(android.os.IBinder token, android.view.RemoteAnimationDefinition definition) 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.writeStrongBinder(token); //第一步是将Binder对象写入Parcel中,会调用acquire_object增加JavaBBinder的强引用计数
if ((definition!=null)) {
_data.writeInt(1);
definition.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_registerRemoteAnimations, _data, _reply, 0); //第二步是进行binder通信
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle(); //第三步是回收Parcel对象,进而会调用release_object减少JavaBBinder的强引用计数
}
}
大体过程可以分为三步:
_data.writeStrongBinder
将Binder对象写入Parcel,其内部会将JavaBBinder写入Parcel(native),并同时增加JavaBBinder的强引用计数。mRemote.transact
将Binder对象传输给对端进程,对端进程接收到数据后会创建相应的BinderProxy对象。_data.recycle
回收Parcel对象,同时减少JavaBBinder的强引用计数。
如果希望通信链路在_data.recycle
后不被破坏,那么必须要在mRemote.transact
里增加JavaBBinder的强引用计数,这实际上也是将JavaBBinder的生命交由BinderProxy管理的过程。接下来,让我们深入到驱动里去一探究竟。
1.3 binder_node对象
进入Binder驱动后,binder_transaction
函数会遍历本次传输的所有Binder对象。接着最关键的一步是binder_translate_binder
,它会根据flat_binder_object生成内核空间中相应的binder_node和binder_ref对象,并将BINDER_TYPE_BINDER的类型更改为BINDER_TYPE_HANDLE,表明我们虽然传输的是一个Binder对象,但是对端拿到的将是Proxy对象。
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
struct flat_binder_object *fp;
fp = to_flat_binder_object(hdr);
ret = binder_translate_binder(fp, t, thread);
...
} break;
binder_translate_binder
首先会通过binder_new_node
创建一个新的binder_node对象,紧接着调用binder_inc_ref_for_node
去创建binder_ref对象。
static int binder_translate_binder(struct flat_binder_object *fp,
struct binder_transaction *t,
struct binder_thread *thread)
{
struct binder_node *node;
struct binder_proc *proc = thread->proc;
struct binder_proc *target_proc = t->to_proc;
struct binder_ref_data rdata;
int ret = 0;
node = binder_get_node(proc, fp->binder);
if (!node) {
node = binder_new_node(proc, fp);
if (!node)
return -ENOMEM;
}
...
ret = binder_inc_ref_for_node(target_proc, node,
fp->hdr.type == BINDER_TYPE_BINDER,
&thread->todo, &rdata);
...
}
binder_inc_ref_for_node
里会做两件事,一是生成对端进程的binder_ref对象,通过它能够找到刚刚创建的binder_node对象;二是调用binder_inc_ref_olocked
来管理binder_node和binder_ref对象的生命周期。
static int binder_inc_ref_for_node(struct binder_proc *proc,
struct binder_node *node,
bool strong,
struct list_head *target_list,
struct binder_ref_data *rdata)
{
struct binder_ref *ref;
struct binder_ref *new_ref = NULL;
int ret = 0;
binder_proc_lock(proc);
ref = binder_get_ref_for_node_olocked(proc, node, NULL);
if (!ref) {
binder_proc_unlock(proc);
new_ref = kzalloc(sizeof(*ref), GFP_KERNEL);
if (!new_ref)
return -ENOMEM;
binder_proc_lock(proc);
ref = binder_get_ref_for_node_olocked(proc, node, new_ref);
}
ret = binder_inc_ref_olocked(ref, strong, target_list);
}
binder_inc_ref_olocked
里既增加binder_ref对象的strong值,也会进一步调用binder_inc_node
增加相应binder_node对象的引用值。binder_ref对象的strong不为0,表示它被人引用无法被销毁。这里之所以增加,是因为接下来type为BINDER_TYPE_HANDLE的数据会写入binder buffer,因此binder buffer会引用binder_ref对象。
static int binder_inc_ref_olocked(struct binder_ref *ref, int strong,
struct list_head *target_list)
{
int ret;
if (strong) {
if (ref->data.strong == 0) {
ret = binder_inc_node(ref->node, 1, 1, target_list);
if (ret)
return ret;
}
ref->data.strong++;
} else {
if (ref->data.weak == 0) {
ret = binder_inc_node(ref->node, 0, 1, target_list);
if (ret)
return ret;
}
ref->data.weak++;
}
return 0;
}
binder_inc_node_nilocked
里会做两件事,一是增加binder_node对象的internal_strong_refs值 ,另一个是往thread的todo列表中新增一个BINDER_WORK_NODE的任务。binder_node
里的引用字段有些复杂,主要是名称具有歧义。其主要使用的字段有internal_strong_refs和local_strong_refs,前者表明binder_node对象被一个binder_ref对象所引用,进一步引申的含义是binder_node对象被另一个进程所引用;后者表明binder_node对象被本进程所引用,它不一定指具体的持有关系,而是在本进程执行某些任务时,binder_node对象不能被释放。
static int binder_inc_node_nilocked(struct binder_node *node, int strong,
int internal,
struct list_head *target_list)
{
struct binder_proc *proc = node->proc;
if (strong) {
if (internal) {
node->internal_strong_refs++;
} else
node->local_strong_refs++;
if (!node->has_strong_ref && target_list) {
struct binder_thread *thread = container_of(target_list,
struct binder_thread, todo);
binder_dequeue_work_ilocked(&node->work);
BUG_ON(&thread->todo != target_list);
binder_enqueue_deferred_thread_work_ilocked(thread,
&node->work);
}
这个被加入到thread todo列表中的任务是一个延迟任务,但不管是同步通信还是异步通信,它们都会在transact结束前完成。
BINDER_WORK_NODE任务处理时会做两件事:
- node的has_weak_ref和has_strong_ref设置为true,pending_weak_ref和pending_strong_ref设置为1,local_weak_refs和local_strong_refs均自增。
- 给用户空间返回BR_INCREFS和BR_ACQUIRE。
那么如何去理解这两件事呢?
首先,给用户空间返回的指令会让用户空间去增加JavaBBinder对象的强引用计数和弱引用计数,保证JavaBBinder在有其他进程引用时不会被销毁。而pending_weak_ref/pending_strong_ref/local_weak_refs/local_strong_refs之所以被置上,均是在等待用户空间操作的完成。当用户空间完成增加引用计数的操作后,会重新回到内核空间,带回BC_INCREFS_DONE和BC_ACQUIRE_DONE的指令,将之前的四个值复位,表明用户空间已经完成这项任务。
case BINDER_WORK_NODE: {
struct binder_node *node = container_of(w, struct binder_node, work);
int strong, weak;
binder_uintptr_t node_ptr = node->ptr;
binder_uintptr_t node_cookie = node->cookie;
int node_debug_id = node->debug_id;
int has_weak_ref;
int has_strong_ref;
void __user *orig_ptr = ptr;
strong = node->internal_strong_refs ||
node->local_strong_refs;
weak = !hlist_empty(&node->refs) ||
node->local_weak_refs ||
node->tmp_refs || strong;
has_strong_ref = node->has_strong_ref;
has_weak_ref = node->has_weak_ref;
if (weak && !has_weak_ref) {
node->has_weak_ref = 1;
node->pending_weak_ref = 1;
node->local_weak_refs++;
}
if (strong && !has_strong_ref) {
node->has_strong_ref = 1;
node->pending_strong_ref = 1;
node->local_strong_refs++;
}
if (weak && !has_weak_ref)
ret = binder_put_node_cmd(
proc, thread, &ptr, node_ptr,
node_cookie, node_debug_id,
BR_INCREFS, "BR_INCREFS");
if (!ret && strong && !has_strong_ref)
ret = binder_put_node_cmd(
proc, thread, &ptr, node_ptr,
node_cookie, node_debug_id,
BR_ACQUIRE, "BR_ACQUIRE");
这样一来,我们就可以保证_data.recycle
结束后,JavaBBinder对象依然不被销毁的效果了。至此,本进程的工作就已经完成了。
让我们来梳理一下各个对象的依赖关系。
- Java层的Binder对象回收时,Native层的JavaBBinderHolder对象才能被销毁。
- Native层的JavaBBinder对象被销毁时,Java层的Binder对象才能从全局引用表中出来,也才能够被GC回收。
- Kernel层的binder_node对象的internal_strong_refs和local_strong_refs值均减为0时,Native层的JavaBBinder对象的强引用计数才能归零,也才能被销毁。
所以对于本进程而言,这些对象的销毁关系仿佛像多米诺骨牌一样,只有前面的倒下,后面的才能够顺次倒下。根据上述依赖关系可知,如果想要销毁这些对象,首先要做的就是将binder_node对象的internal_strong_refs和local_strong_refs的值减为0(local_strong_refs通常为0)。而internal_strong_refs其实记录的就是有多少个进程引用了该Binder对象。
2. Client进程
此时的binder_ref对象已经创建,ref->data.strong的值为1,表明binder buffer正在引用它。当Client进程接收到通信请求后,会先调用IPCThreadState::executeCommand方法。这里会先创建一个局部的Parcel对象buffer
,来接收binder buffer中的数据。一旦buffer
销毁后,binder buffer也将被释放,而此前给binder_ref对象增加的strong值也将减回去。
case BR_TRANSACTION:
{
...
Parcel buffer;
buffer.ipcSetDataReference(
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(binder_size_t), freeBuffer);
...
if (tr.target.ptr) {
error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
&reply, tr.flags);
}
}
上面的transact函数最终会调用Java层的onTransact来进行处理。以下面的代码为例,首先需要通过readStrongBinder
的方法将刚刚传递过来的Binder对象解析出来,解析出来的结果便是一个BinderProxy对象。
case TRANSACTION_bindService:
{
data.enforceInterface(descriptor);
android.app.IApplicationThread _arg0;
_arg0 = android.app.IApplicationThread.Stub.asInterface(data.readStrongBinder());
readStrongBinder
是一个native方法,其内部实现可以分为两步:parcel->readStrongBinder()
和javaObjectForIBinder
。
static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
return javaObjectForIBinder(env, parcel->readStrongBinder());
}
return NULL;
}
parcel->readStrongBinder()
用于根据Parcel中的数据创建BpBinder对象。
303 case BINDER_TYPE_HANDLE: {
304 sp<IBinder> binder =
305 ProcessState::self()->getStrongProxyForHandle(flat->handle);
306 return finishUnflattenBinder(binder, out);
307 }
308 }
getStrongProxyForHandle
中使用BpBinder::create的方法来创建新的BpBinder对象。其中大部分代码是用来防止BpBinder对象leak的,这与本文要讨论的话题无关(或许后面会单独成文),暂且跳过。真正负责对象创建的其实只有最后一行,它做了两件事:一是通过new的方式创建了新的BpBinder对象;二是创建一个sp对象来指向它,这会增加BpBinder对象的强引用计数,并且会回调BpBinder的onFirstRef函数(因为是第一次被引用)。
sp<BpBinder> BpBinder::create(int32_t handle) {
int32_t trackedUid = -1;
if (sCountByUidEnabled) {
trackedUid = IPCThreadState::self()->getCallingUid();
AutoMutex _l(sTrackingLock);
uint32_t trackedValue = sTrackingMap[trackedUid];
if (CC_UNLIKELY(trackedValue & LIMIT_REACHED_MASK)) {
if (sBinderProxyThrottleCreate) {
return nullptr;
}
} else {
if ((trackedValue & COUNTING_VALUE_MASK) >= sBinderProxyCountHighWatermark) {
ALOGE("Too many binder proxy objects sent to uid %d from uid %d (%d proxies held)",
getuid(), trackedUid, trackedValue);
sTrackingMap[trackedUid] |= LIMIT_REACHED_MASK;
if (sLimitCallback) sLimitCallback(trackedUid);
if (sBinderProxyThrottleCreate) {
ALOGI("Throttling binder proxy creates from uid %d in uid %d until binder proxy"
" count drops below %d",
trackedUid, getuid(), sBinderProxyCountLowWatermark);
return nullptr;
}
}
}
sTrackingMap[trackedUid]++;
}
return sp<BpBinder>::make(BinderHandle{handle}, trackedUid);
}
BpBinder的构造函数会调用IPCThreadState的incWeakHandle
函数,它会往驱动中发送BC_INCREFS命令,让驱动去增加binder_ref对象的weak值。
BpBinder::BpBinder(BinderHandle&& handle, int32_t trackedUid) : BpBinder(Handle(handle)) {
mTrackedUid = trackedUid;
IPCThreadState::self()->incWeakHandle(this->binderHandle(), this);
}
onFirstRef函数会调用IPCThreadState的incStrongHandle
函数,它会往驱动中发送BC_ACQUIRE命令,让驱动去增加binder_ref对象的strong值。
void BpBinder::onFirstRef()
{
ALOGV("onFirstRef BpBinder %p handle %d\n", this, binderHandle());
if (CC_UNLIKELY(isRpcBinder())) return;
IPCThreadState* ipc = IPCThreadState::self();
if (ipc) ipc->incStrongHandle(binderHandle(), this);
}
这样一来,BpBinder对象创建了出来,同时对应的binder_ref对象的strong和weak值也都增加了1。上文提到,一旦Parcel对象销毁后,binder buffer也将被释放,而此前给binder_ref对象增加的strong值也将减回去,因此这里增加的strong和weak便可以保证binder_ref对象不被销毁。
BpBinder对象创建出来以后,它会作为参数进入javaObjectForIBinder函数,根据Native层的BpBinder对象创建出Java层的BinderProxy对象。该函数首先会创建一个新的BinderProxyNativeData对象,通过sp持有的方式,将BpBinder的生命掌控在了自己手里。只有BinderProxyNativeData对象销毁时,BpBinder才能够被销毁。接着调用Java层的BinderProxy.getInstance()方法创建出BinderProxy对象。
jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{
BinderProxyNativeData* nativeData = new BinderProxyNativeData();
nativeData->mOrgue = new DeathRecipientList;
nativeData->mObject = val;
jobject object = env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
gBinderProxyOffsets.mGetInstance, (jlong) nativeData, (jlong) val.get());
return object;
}
struct BinderProxyNativeData {
// Both fields are constant and not null once javaObjectForIBinder returns this as
// part of a BinderProxy.
// The native IBinder proxied by this BinderProxy.
sp<IBinder> mObject;
// Death recipients for mObject. Reference counted only because DeathRecipients
// hold a weak reference that can be temporarily promoted.
sp<DeathRecipientList> mOrgue; // Death recipients for mObject.
};
新创建的BinderProxy对象会持有Native层BinderProxyNativeData对象的地址,并通过NativeAllocationRegistry的机制来将二者的生命周期进行捆绑,只有BinderProxy对象被回收后,BinderProxyNativeData对象才能被销毁。
private static BinderProxy getInstance(long nativeData, long iBinder) {
BinderProxy result;
synchronized (sProxyMap) {
try {
result = sProxyMap.get(iBinder);
if (result != null) {
return result;
}
result = new BinderProxy(nativeData);
} catch (Throwable e) {
// We're throwing an exception (probably OOME); don't drop nativeData.
NativeAllocationRegistry.applyFreeFunction(NoImagePreloadHolder.sNativeFinalizer,
nativeData);
throw e;
}
NoImagePreloadHolder.sRegistry.registerNativeAllocation(result, nativeData);
// The registry now owns nativeData, even if registration threw an exception.
sProxyMap.set(iBinder, result);
}
return result;
}
此外还需要注意,新创建的BinderProxy对象会加入sProxyMap,这样传输两次相同的Binder对象才能找到同一个BinderProxy对象。为了不干扰BinderProxy对象正常的生命周期,sProxyMap中采用WeakReference的方式来间接引用BinderProxy对象。
当我们拿到BinderProxy对象后,离通信链路的建立也只有一步之遥了。最后调用asInterface方法,来生成最终的Stub.Proxy对象,它会持有BinderProxy对象的引用。
_arg0 = android.app.IApplicationThread.Stub.asInterface(data.readStrongBinder());
至此,整个Binder通信链路才算建立完全。Server进程中Parcel对象对JavaBBinder对象的引用保证了跨进程引用关系建立前,JavaBBinder对象不会被销毁;Client进程中binder buffer对binder_ref对象的引用保证了跨进程引用关系建立前,binder_ref对象不会被销毁。如下图所示。
当整个过程都结束以后,各中间变量的状态汇总如下:
- JavaBBinder对象的强引用计数为1,弱引用计数为2。
- binder_node对象的internal_strong_refs为1,has_strong_ref和has_weak_ref均为true。
- binder_ref对象的strong为1,weak为1。
- BpBinder对象的强引用计数为1,弱引用计数为1。
如果再将Binder对象发送给另一个进程,那么此时的:
- JavaBBinder对象的强引用计数为2,弱引用计数为3。
- binder_node对象的internal_strong_refs为2,has_strong_ref和has_weak_ref均为true。
销毁过程
一个Binder对象何时可以被回收?答案是它的所有BinderProxy对象都消亡时。销毁过程如下所示:
- 当Client进程中的Stub.Proxy对象不再被使用时,它会被GC回收。
- Stub.Proxy对象被回收后,BinderProxy对象便不再被人引用,因此也会被GC回收。
- 由于NativeAllocationRegistry机制的存在,BinderProxy对象回收时,BinderProxyNativeData对象也被销毁。
- BinderProxyNativeData对象销毁后,BpBinder对象的强引用计数减为0,因此也会被销毁。
- BpBinder的析构过程会调用IPCThreadState的decStrongHandle和decWeakHandle函数,它们会分别向驱动发送BC_RELEASE和BC_DECREFS指令。
- 驱动接收到这两条指令,将会分别减去binder_ref对象的strong值和weak值。当strong值减为0时,相应的binder_node对象的internal_strong_refs也减1。当strong和weak同时为0时,释放binder_ref对象。
- 当binder_node对象的internal_strong_refs减为0时,会将BINDER_WORK_NODE任务发送给Server进程。
- Server进程处理BINDER_WORK_NODE任务时,一方面会释放binder_node对象,另一方面会给用户空间分别返回BR_RELEASE和BR_DECREFS指令。
- 用户空间接收到这两个命令后,会分别调用decStrong和decWeak函数,JavaBBinder的强引用计数-1,弱引用计数-2(decStrong中会同时执行decWeak)。
- JavaBBinder的强引用计数为0,因此被销毁。
- JavaBBinder的析构函数中会将Binder对象从Global Refs列表中清除,因此Binder对象不再被当作GC roots,它会被GC回收。
- 由于NativeAllocationRegistry机制的存在,Binder对象回收时,JavaBBinderHolder对象也被销毁。
整个过程如同消消乐一般,最后所有链路上的对象全被销毁。
结语
实事求是的说,这篇文章是有难度的。理解它需要具备一些背景知识,也需要对Binder通信过程比较了解,否则看起来很可能云里雾里。那既然如此,我为什么要写它呢?原因是有些知识虽然用的人不多,但一旦需要时,这些资料又显得十分稀有。大众爆款做的人很多,小众精品也需要有人耕耘。不管是哪种途径,本质都是希望为中文技术社区做些贡献。