zoukankan      html  css  js  c++  java
  • Binder机制,从Java到C (5. IBinder对象传递形式)

    1.IBinder的传递

    Binder IPC通信中,Binder是通信的媒介Parcel是通信的內容。远程调用过程中,其参数都被打包成Parcel的形式來传递。IBinder对象当然也不例外,在前一篇 Binder机制,从Java到C (4. Parcel) 中说到IBinder对象是能够进行进程间传递的

    下面就看一下IBinder对象在传递过程中会有什么变化。

    在IPC通信的Proxy端,我们经常可以看到下面类似的代码,一些参数都会打包到Parcel中。看下面的data和reply。

     1 public void publishService(IBinder token,
     2             Intent intent, IBinder service) throws RemoteException {
     3         Parcel data = Parcel.obtain();
     4         Parcel reply = Parcel.obtain();
     5         data.writeInterfaceToken(IActivityManager.descriptor);
     6         data.writeStrongBinder(token);
     7         intent.writeToParcel(data, 0);
     8         data.writeStrongBinder(service);
     9         mRemote.transact(PUBLISH_SERVICE_TRANSACTION, data, reply, 0);
    10         reply.readException();
    11         data.recycle();
    12         reply.recycle();
    13     }

    在IPC通信里,android是通过Parcel类的成员函数writeStrongBinder()向Parcel写入IBinder,再通过mRemote发送出去。

    2.writeStrongBinder()

    接下來就看一下writeStrongBinder()这个函数里做了什么。
    /frameworks/base/core/java/android/os/Parcel.java

     1 public final class Parcel {
     2  3     /**
     4      * Write an object into the parcel at the current dataPosition(),
     5      * growing dataCapacity() if needed.
     6      */
     7     public final void writeStrongBinder(IBinder val) {
     8         nativeWriteStrongBinder(mNativePtr, val);
     9     }
    10     private static native void nativeWriteStrongBinder(int nativePtr, IBinder val);
    11 12 }

    看,这里又调用了native方法,我们会发现Java 层的一些Parcel其实只是对C/C++层的一个封裝,大部分操作还是依靠JNI去调用Native的操作。
    那看一下C++里对应的方法:

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

     1 static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jint nativePtr, jobject object)
     2 {
     3     Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
     4     if (parcel != NULL) {
     5         const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
     6         if (err != NO_ERROR) {
     7             signalExceptionForError(env, clazz, err);
     8         }
     9     }
    10 }

    最主要的就是ibinderForJavaObject 这个函数,继续跟踪代码:

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

     1 sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
     2 {
     3     if (obj == NULL) return NULL;
     4 
     5     if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) { //mClass指向Java层中的Binder class
     6         JavaBBinderHolder* jbh = (JavaBBinderHolder*)
     7             env->GetIntField(obj, gBinderOffsets.mObject);
     8         return jbh != NULL ? jbh->get(env, obj) : NULL; //get() 返回一個JavaBBinder,继承自BBinder
     9     }
    10 
    11     if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) { //mClass 指向Java层的BinderProxy class
    12         return (IBinder*)
    13             env->GetIntField(obj, gBinderProxyOffsets.mObject); //返回一个BpBinder,mObject是它的地址值
    14     }
    15     ALOGW("ibinderForJavaObject: %p is not a Binder object", obj);
    16     return NULL;
    17 }

    这个函数呢,就是根据传进来的Java对象找到对应的C++对象,这里的参数obj,可能会指向两种对象:Binder对象或者BinderProxy对象。这两个对象我们在RemoteService那一篇里其实就遇到过了,Binder对象呢就是Service里实现的那个mRemoteBinder,BinderProxy对象呢就是Activity里回调传进来的那个service。不记得的话,可以再看一下那一篇:Binder机制,从Java到C (1. IPC in Application Remote Service)

    接着说,如果传进來的是Binder对象,那么就会把gBinderOffsets.mObject转化成JavaBBinderHolder, 并从中获得一个JavaBBinder对象。JavaBBinder继承自BBinder。(在RemoteService那一篇,Service会在onBind回调里把mRemoteBinder传给AMS,这里传递的就是Binder对象)

    如果传进來的是BinderProxy對象。就会返回一个BpBinder,这个BpBinder的地址值就保存在gBinderProxyOffsets.mObject中。(在RemoteService那一篇,AMS要传递给Acticity的就是BinderProxy对象)

    到这里,你可能会奇怪,为什么Service把Binder对象传递给AMS,后面AMS却是把BinderProxy对象传递给Activity呢?这个我们后面再说,(可以先说一下,其实是因为这些Binder对象都会经过底层传输,那在传输的过程中,Binder模块就会根据不同的情况对这些对象进行转化)

    好吧,那还是接着说,有发送,当然会有接收,再來看一下Parcel的readStrongBinder():


    3.readStrongBinder()

    同样也是JNI调用:

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

     1 static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jint nativePtr)
     2 {
     3       ...
     4       return javaObjectForIBinder(env, parcel->readStrongBinder());
     5 }
     6 
     7 /frameworks/base/core/jni/android_util_Binder.cpp
     8 jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val) (1)
     9 {
    10     if (val == NULL) return NULL;
    11     if (val->checkSubclass(&gBinderOffsets)) { (2)12         // One of our own!
    13         jobject object = static_cast<JavaBBinder*>(val.get())->object();
    14         LOGDEATH("objectForBinder %p: it's our own %p!
    ", val.get(), object);
    15         return object;
    16     }
    17    ...
    18     jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
    19     if (object != NULL) {
    20         jobject res = env->CallObjectMethod(object, gWeakReferenceOffsets.mGet); (3)
    21         if (res != NULL) {
    22             ALOGV("objectForBinder %p: found existing %p!
    ", val.get(), res);
    23             return res;
    24         }
    25        ...
    26     }
    27     object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor); (4)
    28     if (object != NULL) {
    29        ….
    30     }
    31     return object;
    32 }

    (1):这里的val和writeStrongBinder类似,不过是反过來,指向的可能是JavaBBinder或者BpBinder

    (2)如果是JavaBBinder,就会通过成员函數object(),返回一个Java对象,这个对象就是Java层的Binder对象。

    (3)如果是BpBinder,那就先查找是否已經存在需要使用的BinderProxy对象,如果找到就返回引用

    (4)如果沒找到可用的引用,就new 一个BinderProxy对象。

     

    4.Binder Module in Kernel

    实际上,在Client端將参数打包成Parcel后,会发送到kernel的Binder module。在Binder module中,会有两种IBinder类型:BINDER_TYPE_BINDER,BINDER_TYPE_HANDLE。也对应着ava的本地对象和代理对象。

    在Binder module收到数据后,会对不同的情况做进一步处理:
    1.传來的IBinder类型是BINDER_TYPE_BINDER, 会将binder_type转化为BINDER_TYPE_HANDLE;

    (这里理解起来有点别扭,因为只有在不同进程间传递IBinder时,才会把IBinder传递到kernel里,所以传递进來的IBinder必定是传递给別的process,不会是IBinder所在的process,这时,IBinder所在的process和发送目标process不是同一个,就需要改成BINDER_TYPE_HANDLE。如果IBinder是在同一个进程间传递,也不会进入到kernel里。所以一个BINDER_TYPE_BINDER类型的IBinder传进kernel,就必然需要转化成BINDER_TYPE_HANDLE)

    2.传來的IBinder类型是BINDER_TYPE_HANDLE,会判断这个IBinder实体定义的process和发送的目标process是否相同,如果相同,就将binder_type转化成BINDER_TYPE_BINDER。如果不同,保持BINDER_TYPE_HANDLE。

    因为只有不同进程间传递才会把IBinder发送到Binder module,所以在IBinder的传递中,可能会有2中可能。
    1.process A → process B → process A
    2.process A → process B → process C

    这時候,对应的IBinder类型就会是:
    1.BINDER_TYPE_BINDER → BINDER_TYPE_HANDLE → BINDER_TYPE_BINDER
    2.BINDER_TYPE_BINDER → BINDER_TYPE_HANDLE → BINDER_TYPE_HANDLE

     

    5.再看 Bind service

    实际上,bind一个service的过程,也就是上述的第2中情況。

  • 相关阅读:
    nginx rewrite 伪静态重写学习笔记
    正则表达式相关知识
    rpm的含义
    find命令的使用
    chmod的运用方式
    [GO]数组的比较和赋值
    [GO]二维数组的介绍
    [GO]变量内存和变量地址
    [GO]给导入包起别名
    阿里云负载均衡SLB 七层https协议 nginx 获取真实IP
  • 原文地址:https://www.cnblogs.com/zhangxinyan/p/3487866.html
Copyright © 2011-2022 走看看