Binder通信传输callback时,为什么能实现unRegistCallback?

问题描述

Android开发中,客户端通过Binder向服务端注册 / 去注册 callback,是我们常用的开发方式。了解Binder通信机制的同学应该会知道,我们通过Binder,把callback从客户端发送到服务端时,服务端获得的,其实是一份反序列化后new出来的IInterface实例。例如以下服务端接收代码:

status_t BnPreviewService::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch(code) {
        case START_PREVIEW: {
            CHECK_INTERFACE(IPreviewService, data, reply);
            
            sp st =
                interface_cast(data.readStrongBinder());
            int channel = data.readInt32();
            reply->writeInt32(startPreview(st,channel));
            return NO_ERROR;
        } break;

通过Binder,服务端接收到客户端发来的序列化打包的Parcel,用于构建一个新的IGraphicBufferProducer实例。
即使重复将同一个callback实例通过Binder发向服务端,服务端都会构建出一个新的IGraphicBufferProducer实例。这时候对比这两个实例,它们指向的地址必然是不同的。

我们为什么能够实现callback的去注册?

google为我们封装了一个管理callback的类:RemoteCallbackList。我们的binder服务可以利用它来管理回调。示例如下:

public class RemoteService extends Service {

    final RemoteCallbackList mCallbacks
            = new RemoteCallbackList();

    private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
        public void registerCallback(IRemoteServiceCallback cb) {
            if (cb != null) mCallbacks.register(cb);
        }
        public void unregisterCallback(IRemoteServiceCallback cb) {
            if (cb != null) mCallbacks.unregister(cb);
        }
    };
}

我们看看它是如何实现callback的注册和去注册的。

//RemoteCallbackList.java
    ArrayMap mCallbacks = new ArrayMap();

    public boolean register(E callback, Object cookie) {
        synchronized (mCallbacks) {
            if (mKilled) {
                return false;
            }
            // Flag unusual case that could be caused by a leak. b/36778087
            logExcessiveCallbacks();
            IBinder binder = callback.asBinder();
            try {
                Callback cb = new Callback(callback, cookie);
                binder.linkToDeath(cb, 0);
                mCallbacks.put(binder, cb);
                return true;
            } catch (RemoteException e) {
                return false;
            }
        }
    }

最核心的两行:
1,通过IInterface接口的public IBinder asBinder()方法,获取IBinder接口的实例:

IBinder binder = callback.asBinder();

2,存储IBinder实例:

mCallbacks.put(binder, cb);

再看去注册回调的实现:

    public boolean unregister(E callback) {
        synchronized (mCallbacks) {
            Callback cb = mCallbacks.remove(callback.asBinder());
            if (cb != null) {
                cb.mCallback.asBinder().unlinkToDeath(cb, 0);
                return true;
            }
            return false;
        }
    }

关键在于下面这句:

Callback cb = mCallbacks.remove(callback.asBinder());

我们发现,RemoteCallbackList存储的并不是传入的callback实例,而是通过asBinder接口获取到的IBinder实例。java层的IBinder实例其实保存了native的BBinder指针地址。

//Binder.java
    /**
     * Raw native pointer to JavaBBinderHolder object. Owned by this Java object. Not null.
     */
    private final long mObject;

这里就有两个疑问:

1,注册和去注册时,asBinder()返回的IBinder实例为什么是同一个?
2,IBinder实例为什么能作为Map的key进行正确的比较?

1,注册和去注册时,asBinder()返回的IBinder实例为什么是同一个?

先看注册callback时,从aidl文件自动生成的代码:

@Override public int registerServerCallBack(IMyCallback client) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((client!=null))?(client.asBinder()):(null)));
mRemote.transact(Stub.TRANSACTION_registerServerCallBack, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}

可以看到,传递的aidl接口序列化时调用了以下接口来打包callback中的IBinder:

Parcel.writeStrongBinder

而Parcel的这个接口,实际上通过JNI调用了Native接口:

    public final void writeStrongBinder(IBinder val) {
        nativeWriteStrongBinder(mNativePtr, val);
    }

跟踪代码:

/frameworks/base/core/jni/android_os_Parcel.cpp

static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jint nativePtr, jobject object)
{
    // 使用Native的Parcel方法进行实际的序列化
    Parcel* parcel = reinterpret_cast(nativePtr);
    if (parcel != NULL) {
        const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
        if (err != NO_ERROR) {
            signalExceptionForError(env, clazz, err);
        }
    }
}

/frameworks/base/core/jni/android_util_Binder.cpp

sp ibinderForJavaObject(JNIEnv* env, jobject obj)
{
    if (obj == NULL) return NULL;

    if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) { //mClass指向Java层中的Binder class
        JavaBBinderHolder* jbh = (JavaBBinderHolder*)
            env->GetIntField(obj, gBinderOffsets.mObject);
        return jbh != NULL ? jbh->get(env, obj) : NULL; //get() 返回一個JavaBBinder,继承自BBinder
    }

    if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) { //mClass 指向Java层的BinderProxy class
        return (IBinder*)
            env->GetIntField(obj, gBinderProxyOffsets.mObject); //返回一个BpBinder,mObject是它的地址值
    }
    ALOGW("ibinderForJavaObject: %p is not a Binder object", obj);
    return NULL;
}

序列化时,客户端(Service的调用者)传入的callbakc的IBinder实际是BBinder类。Native的Parcel真正地序列化:

// framework/native/libs/binder/Parcel.cpp

status_t flatten_binder(const sp& /*proc*/,
    const sp& binder, Parcel* out)
{
    flat_binder_object obj;

    if (IPCThreadState::self()->backgroundSchedulingDisabled()) {
        /* minimum priority for all nodes is nice 0 */
        obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
    } else {
        /* minimum priority for all nodes is MAX_NICE(19) */
        obj.flags = 0x13 | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    }

    if (binder != NULL) {
        IBinder *local = binder->localBinder();
        if (!local) {
            BpBinder *proxy = binder->remoteBinder();
            if (proxy == NULL) {
                ALOGE("null proxy");
            }
            const int32_t handle = proxy ? proxy->handle() : 0;
            obj.hdr.type = BINDER_TYPE_HANDLE;
            obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
            obj.handle = handle;
            obj.cookie = 0;
        } else {
            // 客户端传入的是BBinder,走这个分支,保存BBinder地址
            obj.hdr.type = BINDER_TYPE_BINDER;
            obj.binder = reinterpret_cast(local->getWeakRefs());
            obj.cookie = reinterpret_cast(local);
        }
    } else {
        obj.hdr.type = BINDER_TYPE_BINDER;
        obj.binder = 0;
        obj.cookie = 0;
    }

    return finish_flatten_binder(binder, obj, out);
}

结论:
callback传递时,序列化的过程就是将BBinder的地址放进了Pacel中。

服务端反序列化代码:

case TRANSACTION_registerServerCallBack:
{
data.enforceInterface(descriptor);
IMyCallback _arg0;
_arg0 = IMyCallback.Stub.asInterface(data.readStrongBinder());
int _result = this.registerServerCallBack(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}

Native的Parcel::readStrongBinder()其实就是返回了一个IBinder对象的地址。

status_t Parcel::readNullableStrongBinder(sp* val) const
{
    return unflatten_binder(ProcessState::self(), *this, val);
}

sp Parcel::readStrongBinder() const
{
    sp val;
    // Note that a lot of code in Android reads binders by hand with this
    // method, and that code has historically been ok with getting nullptr
    // back (while ignoring error codes).
    readNullableStrongBinder(&val);
    return val;
}

status_t unflatten_binder(const sp& proc,
    const Parcel& in, sp* out)
{
    const flat_binder_object* flat = in.readObject(false);

    if (flat) {
        switch (flat->hdr.type) {
            case BINDER_TYPE_BINDER:
                // 通过发过来的地址,强转成一个指向该地址的IBinder对象
                *out = reinterpret_cast(flat->cookie);
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_HANDLE:
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast(out->get()), *flat, in);
        }
    }
    return BAD_TYPE;
}

Java层也可以获得同一个对象,这个对象是使用内存地址转出来:

jobject javaObjectForIBinder(JNIEnv* env, const sp& val) 
{
    if (val == NULL) return NULL;
    if (val->checkSubclass(&gBinderOffsets)) { //  如果发过来的是JavaBBinder(BBinder的java层对象)
        jobject object = static_cast(val.get())->object();  // 使用发过来的IBinder地址,转一个JavaBBinder给到java层,而不是new出来
        LOGDEATH("objectForBinder %p: it's our own %p!\n", val.get(), object);
        return object;
    }
   ...
    jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
    if (object != NULL) {
        jobject res = env->CallObjectMethod(object, gWeakReferenceOffsets.mGet); (3)
        if (res != NULL) {
            ALOGV("objectForBinder %p: found existing %p!\n", val.get(), res);
            return res;
        }
       ...
    }
    object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor); (4)
    if (object != NULL) {
       ….
    }
    return object;
}

调用Stub.asInterface后,得到的就是Proxy了。参考以下代码:

        public static android.os.IMessenger asInterface(android.os.IBinder obj)
        {
            if ((obj==null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof android.os.IMessenger))) {
                return ((android.os.IMessenger)iin);
            }
            return new android.os.IMessenger.Stub.Proxy(obj);
        }

Proxy会保存BBinder的地址到mRemote:


        private static class Proxy implements android.os.IMessenger
        {
            private android.os.IBinder mRemote;
            Proxy(android.os.IBinder remote)
            {
            mRemote = remote;
            }
            
            @Override public android.os.IBinder asBinder()
            {
                return mRemote;
            }

所以,属于同一个BBinder的Proxy,通过asBinder方法得到的IBinder对象中,保存的BBinder的地址都是一样的。

2,为什么IBinder实例能作为Map的key进行正确的比较?

我们经常会看到一个说法:如果想将自定义类对象作为Map中的key,需要是在类中覆写equals方法和hashCode方法。但其实这句话有一个前提,就是想让两个不同的实例被识别为同一个key时,才需要这么做。例如我们想让两个拥有相同id的Student实例被识别为同一个key:

class Student {
public String name;
public int id;

public int hashCode() {
        return id;
    }
}

public boolean equals(Student st) {
        return (this.id == st.id);
    }
}

如果我们的自定义类不覆写这两个方法,那么Map中调用的就是Object类中的实现:

public int hashCode() {
        return identityHashCode(this);
    }
public boolean equals(Object obj) {
        return (this == obj);
    }

简单粗暴的使用物理地址来生成Hash值。
我们已经知道,Proxy.asBinder能够指向对应的IBinder对象的地址,也就是说,regist和unRegist时发送同一个callback,它们asBinder的值是同一个实例,即物理地址是相同的。所以Map可以使用IBinder来作为key值。

你可能感兴趣的:(Binder通信传输callback时,为什么能实现unRegistCallback?)