Android跨进程通信IPC之6——Parcel--Binder对象的写入和读出
移步系列Android跨进程通信IPC系列
Parcel类详解
每一个java端的Binder对象(服务端)在初始化时都会对应一个native对象,类型是BBinder,它继承于IBinder类
1 Binder对象的写入
时序图
5713484-9635f897d3851952.jpeg
通过 Parcel的writeStrongBinder方法将Binder对象序列化:
1.1 Parcel.writeStrongBinder(IBinder)
/Parcel.java /** * Write an object into the parcel at the current dataPosition(), * growing dataCapacity() if needed. */ public final void writeStrongBinder(IBinder val) { nativeWriteStrongBinder(mNativePtr, val); } private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);
调用了native的方法nativeWriteStrongBinder(long,IBinder),这个方法对应JNI的android_os_Parcel_writeStrongBinder()函数
1.2 android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
代码在android_os_Parcel.cpp 298行
static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object){ 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); } }}
- 这里说下ibinderForJavaObject()函数,返回的是BBinder对象(实际上是JavaBBinder,它继承自BBinder)
- 然后调用了Parcel-Native的writeStrongBinder函数
1.3 Parcel::writeStrongBinder
代码在Parcel.cpp 872行
status_t Parcel::writeStrongBinder(const sp& val){ return flatten_binder(ProcessState::self(), val, this);}
主要是调用了flatten_binder()函数
这里说一下ProcessState::self() 是获取ProcessState的单例方法
1.4 Parcel::flatten_binder(const sp& /proc/, const sp& binder, Parcel* out)
代码在Parcel.cpp 205行
status_t flatten_binder(const sp& /*proc*/, const sp& binder, Parcel* out){ flat_binder_object obj; obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; if (binder != NULL) { //JavaBBinder返回的是this,也就是自己 IBinder *local = binder->localBinder(); //不是本地进程,即跨进程 if (!local) { //分支一,如果local为空 BpBinder *proxy = binder->remoteBinder(); if (proxy == NULL) { ALOGE("null proxy"); } const int32_t handle = proxy ? proxy->handle() : 0; obj.type = BINDER_TYPE_HANDLE; obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */ obj.handle = handle; obj.cookie = 0; } else { //分支二,local不为空 //写入JavaBBinder将对应这一段 obj.type = BINDER_TYPE_BINDER; // 弱引用对象 obj.binder = reinterpret_cast(local->getWeakRefs()); // this 对象,BBinder本身 obj.cookie = reinterpret_cast(local); } } else { obj.type = BINDER_TYPE_BINDER; obj.binder = 0; obj.cookie = 0; } return finish_flatten_binder(binder, obj, out);}
这里主要是分别是本地Binder还是远程Binder,对两种Binder采取了两种不同的方式。
- Binder如果是JavaBBinder,则它的localBinder会返回localBinder。
- Binder如果是BpBinder,则它的localBinder会返回null
通过上文我们知道ibinderForJavaObject就返回JavaBBinder。所以我们知道走入分支二
flat_binder_object是Binder写入对象的结构体,它对应着Binder。
flat_binder_object的handle表示Binder对象在Binder驱动中的标志,比如ServiceManager的handle为0。
flat_binder_object的type表示当前传输的Binder是本地的(同进程),还是一个Proxy(跨进程)。
通过上面代码我们知道这里取得的flat_binder_object对应的值如下
- type为BINDER_TYPE_BINDER
- binder为reinterpret_cast(local->getWeakRefs());
- cookie为reinterpret_cast(local)
binder,cookie保存着Binder对象的指针。最后调用了finish_flatten_binder()函数
1.5 Parcel::finish_flatten_binder()函数
代码在Parcel.cpp 199行
inline static status_t finish_flatten_binder( const sp& /*binder*/, const flat_binder_object& flat, Parcel* out){ return out->writeObject(flat, false);}
finish_flatten_binder()函数主要是调用writeObject()函数将flat_binder_object写入到out里面里面,最终写入到Binder驱动中
1.6 Parcel::writeObject()函数
代码在Parcel.cpp 1035行
status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData){ const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity; const bool enoughObjects = mObjectsSize < mObjectsCapacity; //分支一 if (enoughData && enoughObjects) {restart_write: // mObjects 数据写入 *reinterpret_cast(mData+mDataPos) = val; // remember if it's a file descriptor if (val.type == BINDER_TYPE_FD) { if (!mAllowFds) { // fail before modifying our object index return FDS_NOT_ALLOWED; } mHasFds = mFdsKnown = true; } // Need to write meta-data? if (nullMetaData || val.binder != 0) { mObjects[mObjectsSize] = mDataPos; acquire_object(ProcessState::self(), val, this, &mOpenAshmemSize); mObjectsSize++; } return finishWrite(sizeof(flat_binder_object)); } //分支二 if (!enoughData) { const status_t err = growData(sizeof(val)); if (err != NO_ERROR) return err; } //分支三 if (!enoughObjects) { size_t newSize = ((mObjectsSize+2)*3)/2; if (newSize < mObjectsSize) return NO_MEMORY; // overflow binder_size_t* objects = (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t)); if (objects == NULL) return NO_MEMORY; mObjects = objects; mObjectsCapacity = newSize; } goto restart_write;}
三个分支
- 如果mData和mObjects空间足够,则走分支一
- 如果mData空间不足,则扩展空间,growData()函数看前面
- 如果mObjects空间不足,则扩展空间,和mData空间扩展基本相似
调用 * reinterpret_cast
最后调用finishWrite()函数,就是调整调整 mDataPos 和 mDataSize,上面已经说过了,我就跟踪了。至此整体写入流程已经完成了。
2 Binder对象的读出
Parcel对象的读出,首先还是在Parcel.java这个类里面,对应的方法是
时序图如下:
5713484-ad4370e8a1fdf7a8.png
2.1 Parcel.readStrongBinder()方法
/** * Read an object from the parcel at the current dataPosition(). */ public final IBinder readStrongBinder() { return nativeReadStrongBinder(mNativePtr); }
readStrongBinder()方法内部调用了native的nativeReadStrongBinder()方法,
2.2 Parcel.nativeReadStrongBinder()方法
private static native IBinder nativeReadStrongBinder(long nativePtr);
native方法又对应这个JNI的一个方法,通过上文我们知道,对应的是 /frameworks/base/core/jni/android_os_Parcel.cpp的android_os_Parcel_readStrongBinder()函数
2.3 android_os_Parcel_readStrongBinder()函数
代码在android_os_Parcel.cpp 429行
static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr){ Parcel* parcel = reinterpret_cast(nativePtr); if (parcel != NULL) { return javaObjectForIBinder(env, parcel->readStrongBinder()); } return NULL;}
函数里面先调用了Parcel-Native的readStrongBinder()函数,然后又用这个函数的返回值作为参数调用了javaObjectForIBinder()函数。
2.4 readStrongBinder()函数
代码在Parcel.cpp 1134行
sp Parcel::readStrongBinder() const{ sp val; unflatten_binder(ProcessState::self(), *this, &val); return val;}
主要就是调用了unflatten_binder()函数
readStrongBinder 其实挺简单的,是本地的可以直接用,远程的那个 getStrongProxyForHandle 也是放到后面 ServiceManager 再细说。到这里目标进程就收到原始进程传递过来的 binder 对象了,然后可以转化为 binder 的 interface 调用对应的 IPC 接口。
2.5 unflatten_binder()函数
代码在android_os_Parcel.cpp 293行
status_t unflatten_binder(const sp& proc, const Parcel& in, sp* out){ const flat_binder_object* flat = in.readObject(false); if (flat) { switch (flat->type) { case BINDER_TYPE_BINDER: //如果是Bn的话,本地直接强转 *out = reinterpret_cast(flat->cookie); return finish_unflatten_binder(NULL, *flat, in); case BINDER_TYPE_HANDLE: //如果是Bp的话,要通过handle构造一个远程的代理对象 *out = proc->getStrongProxyForHandle(flat->handle); return finish_unflatten_binder( static_cast(out->get()), *flat, in); } } return BAD_TYPE;}
这里说下这里的两个分支
- BINDER_TYPE_BINDER分支:是Bn意味着是同一个进程
- BINDER_TYPE_HANDLE分支:是Bp意味着是跨进程
再来分下一下这个函数,主要就是两个两个流程
-
- 从Binder驱动中读取一个flat_binder_object对象flat
-
- 根据flat对象的type值来分别处理。如果是BINDER_TYPE_BINDER,则使用cookie中的值,强制转换成指针。如果是BINDER_TYPE_HANDLE,则使用Proxy,即通过getStringProxyForHandle()函数,并根据handle创建BpBinder。
ProcessState::getStrongProxyForHandle()函数后续讲解Binder的时候再详细讲解,这里就不说了。那我们来看一下finish_unflatten_binder()
2.6 finish_unflatten_binder()函数
代码在android_os_Parcel.cpp 286行
inline static status_t finish_unflatten_binder( BpBinder* /*proxy*/, const flat_binder_object& /*flat*/, const Parcel& /*in*/){ return NO_ERROR;}
返回NO_ERROR。这时候我们回到了javaObjectForIBinder()函数。
2.7 javaObjectForIBinder()函数
javaObjectgForBinder与ibinderForJavaObject相对应的,把IBinder对象转换成对应的Java层的Object。这个函数是关键。
代码在android_os_Parcel.cpp 547行
jobject javaObjectForIBinder(JNIEnv* env, const sp& val){ if (val == NULL) return NULL; // One of our own! if (val->checkSubclass(&gBinderOffsets)) { //如果是本地的,那么会直接进入这个部分代码,因为这个val //是写入的时候的同一个对象,gBinderOffsets也是一致的。如 //果val是一种Poxy对象,则不然,会继续往下执行,找到一个 //Proxy对象 // One of our own! jobject object = static_cast(val.get())->object(); LOGDEATH("objectForBinder %p: it's our own %p!\n", val.get(), object); return object; } // For the rest of the function we will hold this lock, to serialize // looking/creation of Java proxies for native Binder proxies. AutoMutex _l(mProxyLock); // Someone else's... do we know about it? // BpBinder没有带proxy过来 jobject object = (jobject)val->findObject(&gBinderProxyOffsets); if (object != NULL) { jobject res = jniGetReferent(env, object); if (res != NULL) { ALOGV("objectForBinder %p: found existing %p!\n", val.get(), res); return res; } LOGDEATH("Proxy object %p of IBinder %p no longer in working set!!!", object, val.get()); android_atomic_dec(&gNumProxyRefs); val->detachObject(&gBinderProxyOffsets); env->DeleteGlobalRef(object); } // 创建一个proxy object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor); if (object != NULL) { // 给object的相关字段赋值 LOGDEATH("objectForBinder %p: created new proxy %p !\n", val.get(), object); // The proxy holds a reference to the native object. // 把BpBinder(0) 赋值给BinderProxy的mObject env->SetLongField(object, gBinderProxyOffsets.mObject, (jlong)val.get()); val->incStrong((void*)javaObjectForIBinder); // The native object needs to hold a weak reference back to the // proxy, so we can retrieve the same proxy if it is still active. jobject refObject = env->NewGlobalRef( env->GetObjectField(object, gBinderProxyOffsets.mSelf)); val->attachObject(&gBinderProxyOffsets, refObject, jnienv_to_javavm(env), proxy_cleanup); // Also remember the death recipients registered on this proxy sp drl = new DeathRecipientList; drl->incStrong((void*)javaObjectForIBinder); env->SetLongField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast(drl.get())); // Note that a new object reference has been created. android_atomic_inc(&gNumProxyRefs); incRefsCreated(env); } return object;}
- 首先判断是不是同一个进程,如果是同一个进程,则val是JavaBBinder。那么在checkSubclass()函数中,它所包含的gBinderOffsets指针参数传入的gBinderOffsets的指针必然是同一个值,则满足if条件,直接将指针强制转化为JavaBBinder,返回对应的jobject。如果是不是同一个进程,那么val也就是BpBinder。
- 其次,在BpBinder对象中查找是否保存相关的BinderProxy的对象,如果有,向Java层返回这个对象。如果没有,则创建一个BinderProxy对象,并将新创建的BinderProxy对象,attach到BpBinder对象中。
在构造Java对象的时候,上面用到了 binderproxy_offsets_t 结构体 ,那我们就来看下这个结构体
2.8 binderproxy_offsets_t 结构体
代码在android_os_Parcel.cpp 95行
static struct binderproxy_offsets_t{ // Class state. jclass mClass; jmethodID mConstructor; jmethodID mSendDeathNotice; // Object state. jfieldID mObject; jfieldID mSelf; jfieldID mOrgue;} gBinderProxyOffsets;
gBinderProxyOffsets的初始化是在虚拟机启动的时候(即在 AndroidRuntime::start),最终赋值是在android_os_Parcel.cpp 的中1254行的函数
int_register_android_os_BinderProxy()中,代码如下
static int int_register_android_os_BinderProxy(JNIEnv* env){ jclass clazz = FindClassOrDie(env, "java/lang/Error");1257 gErrorOffsets.mClass = MakeGlobalRefOrDie(env, clazz); clazz = FindClassOrDie(env, kBinderProxyPathName); gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz); gBinderProxyOffsets.mConstructor = GetMethodIDOrDie(env, clazz, "", "()V"); gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice", "(Landroid/os/IBinder$DeathRecipient;)V"); gBinderProxyOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J"); gBinderProxyOffsets.mSelf = GetFieldIDOrDie(env, clazz, "mSelf", "Ljava/lang/ref/WeakReference;"); gBinderProxyOffsets.mOrgue = GetFieldIDOrDie(env, clazz, "mOrgue", "J"); clazz = FindClassOrDie(env, "java/lang/Class"); gClassOffsets.mGetName = GetMethodIDOrDie(env, clazz, "getName", "()Ljava/lang/String;"); return RegisterMethodsOrDie( env, kBinderProxyPathName, gBinderProxyMethods, NELEM(gBinderProxyMethods));}
通过上面代码我们知道这个类型是android.os.BinderProxy,也就是说代理端Java层的对象是android.os.BinderProxy
2.9
- 每个进程都会保存多个当前调用过的BpBinder对象
- 每个BpBinder对象都会保存与之对应的Java层的BinderProxy
将创建的BinderProxy attach到BpBinder的意义在于,通过这种方式,Java应用层偏饭获取同一个Service的IBinder时,获取的是同一个BinderProxy。
3 Parcel读取写入总结
- 上面介绍了Parcel整个写入读取的流程,最后代替Binder传输的是
flat_binder_object。 - 在Parcel-Native中,根据跨进程和非跨进程,flat_binder_object的值是不一样的:跨进程的时候flat_binder_object的type是BINDER_TYPE_HANDLE;非跨进程的时候flat_binder_object的type是BINDER_TYPE_BINDER。
- 客户端的Parcel读取Binder的时候,根据flat_binder_object的type值进行区分对待,返回不同的内容。而写入的时候也是一样的,根据是否是Proxy,来决定写入HANDLE还是BINDER。
- 最终这些内容都会通过ioctl与Binder驱动进行数据通信。所以最终处理不同进程间的Binder数据传输处理也只能是Binder驱动了。
- Binder对象传入Binder驱动最底层是转化为flat_binder_object传递的。
- Parcel是根据从驱动中读取的数据做出不同的处理,如果从Binder驱动中取出的flat_binder_object的type为BINDER_TYPE_HANDLE,则创建BpBinder,在Java层创建的BinderProxy返回;
- 如果读出的flat_binder_object的type为BINDER_TYPE_BINDER则直接使用cookie的指针,将它装置转为化JavaBBinder,在Java层为原来的Service的Binder对象(相同进程)。
- 在 /frameworks/base/core/jni/android_util_Binder.cpp 中有两个函数分别是ibinderForJavaObject()函数与javaObjectForIBinder()函数是相互对应的,一个是把Native中对应的IBinder对象化为Java对象,一个是将Java的对象转为化Native中对应的IBinder对象
参考
Android跨进程通信IPC之4——AndroidIPC基础2
更多相关文章
- Android(安卓)JNI 高级篇
- quick cocos2d-x 与android实现更换头像
- Android(安卓)EventBus使用
- Drawable和Bitmap转换
- Android开发重修
- android viewflipper和tabhost效率对比
- Android中关于Volley的使用(三)认识Volley的架构
- Android(安卓)SharedPreferences的使用.
- Timber 源码解析