问题描述
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值。