Android FrameWork——Binder机制详解(2)

6.前面5个段落我主要说明了BinderProxy是如何把数据发送出去的,Ok, 那么接下来,我们肯定想要知道服务端是怎么接收数据并传递给相应的BBinder进行处理的,有没有注意到前面waitForResponse我标注为蓝 色的代码,这给我们一个启示,也许接收返回数据(进程作为客户端)和接收命令(进程作为服务端)处理的是同一个函数,但这是我的一个猜测,而实际上我参阅 其它blog和代码后并非这么回事,waitForResponse只在客户端发送完数据等待接收数据才被调用的,那么服务端是怎么接收数据的呢?做过 socket编程的同仁们可能知道,服务端为实现接收数据和链接,一般会启动一个监听线程去监听客户端发过来的数据,同样android进程通信的服务端 也是这么做的,在这里我先给你看一个Debug线程图:

这是一个简单的Android应用进程Debug图,有没有看到两个Binder Thread线程,每个应用都包含Binder Thread线程,不信你可以试试,它就是负责监听来自Binder驱动的消息的,那么这两个线程在什么时候被起来的呢,这又是我的一个疑问,我也不清楚 不同Activity应用的Binder Thread是在哪里被起来的,我后面有待研究,不过System_process进程我倒是清楚它是如何启动这两个线程的,在 init1->system_init() @System_init.cpp函数最后:
    if (proc->supportsProcesses()) {
        LOGI("System server: entering thread pool.\n");
        ProcessState::self()->startThreadPool();//启动Binder监听线程
        IPCThreadState::self()->joinThreadPool();
        LOGI("System server: exiting thread pool.\n");
    }
IPCThreadState::self()函数前面我们已经讲过了,它是线程绑定对象,ProcessState::self函数我列出如下
sp<ProcessState> ProcessState::self()
{
    if (gProcess != NULL) return gProcess;
   
    AutoMutex _l(gProcessMutex);
    if (gProcess == NULL) gProcess = new ProcessState;
    return gProcess;
}
显然ProcessState是单例的,也即每个进程拥有一个ProcessState对象,而每个线程拥有一个IPCThreadState对象,关于 ProcessState,IPCThreadState,网上有一篇转高焕堂先生的blog,建议你此时插读一下,我不作详细说明:
認識Android的ProcessState類別和物件:
http://www.android1.net/Topic.aspx?BoardID=31&TopicID=1897

                                       (图2:摘自高焕堂先生课件)

ProcessState负责打开Binder驱动,与Binder驱动进行通信,而IPCStateThread负责每个具体线程IPC数据读写

7.服务端监听线程的构建
服务端具体监听线程是怎样构建的的了,前面我只说了是通过ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool();这两个函数创建的,网上很多blog也只是简单说一下,具体 是怎样一个过程呢?我这里进行一下说明:
void ProcessState::startThreadPool()
{
    AutoMutex _l(mLock);
    if (!mThreadPoolStarted) {
        mThreadPoolStarted = true;
        spawnPooledThread(true);
    }
}
//////////////////////////////////////
//isMain==true
void ProcessState::spawnPooledThread(bool isMain)
{
    if (mThreadPoolStarted) {
        int32_t s = android_atomic_add(1, &mThreadPoolSeq);
        char buf[32];
        sprintf(buf, "Binder Thread #%d", s);
        LOGV("Spawning new pooled thread, name=%s\n", buf);
        sp<Thread> t = new PoolThread(isMain);
        t->run(buf);
    }
}
注意线程的创建是在run方法中,run方法并不像java中Thread.run是新线程的工作函数,它仍在当前线程中执行,PoolThread并没有覆盖父类Thread.run方法,因此它执行的是Thread.run方法:
status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
 。。。
//这个时候才真正创建一个新的线程,新线程的工作方法是_threadLoop,它仍是父类Thread的一个方法
        res = createThreadEtc(_threadLoop,
                this, name, priority, stack, &mThread);
 。。。
}
////////////////////////////////////////////
//新线程工作方法
int Thread::_threadLoop(void* user)
{
。。。
    do {
//调用虚函数threadLoop(),该函数被PoolThread实现
            result = self->threadLoop();
        }
。。。
    } while(strong != 0);   
    return 0;
}
////////////////////////////////////////////
//PoolThread成员方法
    virtual bool threadLoop()
    {
        IPCThreadState::self()->joinThreadPool(mIsMain);//真正线程工作函数
        return false;
    }
通过上面的代码我们看出,ProcessState::self()->startThreadPool();创建了一个新的线程,并且新线程的工 作函数 IPCThreadState::self()->joinThreadPool(true);紧接着当前线程又调用了 IPCThreadState::self()->joinThreadPool(true);我这里是以system_process进程为例, 那说明system_process至少有两个binder线程监听Binder驱动消息,接下来我们仔细看一下 joinThreadPool(true)函数的实现:
//服务端线程工作函数
void IPCThreadState::joinThreadPool(bool isMain)
{
    mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);//通知驱动开始消息监听??
   androidSetThreadSchedulingGroup(mMyThreadId, ANDROID_TGROUP_DEFAULT);//加入默认线程组??

   status_t result;

   do {

       int32_t cmd;

       。。。
       // now get the next command to be processed, waiting if necessary

       result = talkWithDriver();

       if (result >= NO_ERROR) {

           size_t IN = mIn.dataAvail();

           if (IN < sizeof(int32_t)) continue;

           cmd = mIn.readInt32();//读取命令

           }
           result = executeCommand(cmd);//执行命令

       }    

       if(result == TIMED_OUT && !isMain) {

           break;

       }

   } while (result != -ECONNREFUSED && result != -EBADF);

   mOut.writeInt32(BC_EXIT_LOOPER);//通知驱动结束消息监听

   talkWithDriver(false);

}
通过while循环调用IPCThreadState.mIn读取cmd并执行,这我有一个疑惑就是多个线程去透过IPCThreadState.mIn读取驱动消息会不会存在问题?这个暂且mark一下,以后再分析
到此,我们总算了解服务端的消息监听机制,证明并不是如段落6我猜测的IPCThreadState.waitForResponse去接收消息的,而是 IPCThreadState::joinThreadPool中直接透过IPCThreadState.mIn读取消息的。

8.消息处理IPCThreadState::executeCommand(int32_t cmd)
走到这一步视乎离目标已经不远了,回到我们原来的问题,别让我们身陷代码堆栈而迷失了方向,我们前面做的这些工作,目的都是为了客户端的BpBinder 对象能够调到服务端的BBinder对象,而在BpBinder中有一个mHandle参数指定了服务端的Binder通信地址,因此消息才得以发送到服 务端,现在我们有一个问题要解决就是,服务端可能存在多个BBinder对象,我们接收消息后,如何找到对应的BBinder对象把消息传给它处理了,我 们先看消息处理函数
executeCommand:
status_t IPCThreadState::executeCommand(int32_t cmd)
{
    BBinder* obj;//BBinder对象地址
    RefBase::weakref_type* refs;
    status_t result = NO_ERROR;
   
    switch (cmd) {
    case BR_ERROR:
        ...
        ...
    //这是BC_TRANSACTION消息到服务端对应的cmd(客户端发送的是cmd==BC_TRANSACTION,服务端cmd变成BR_TRANSACTION?待研究)
    case BR_TRANSACTION:
        {
            //构造接收消息结构体,与前面客户端writeTransactionData构造的传输数据包一致的结构体
            binder_transaction_data tr;
            result = mIn.read(&tr, sizeof(tr));//读取消息到结构体tr
            LOG_ASSERT(result == NO_ERROR,
                "Not enough command data for brTRANSACTION");
            if (result != NO_ERROR) break;
           
            Parcel buffer;
            buffer.ipcSetDataReference(
                reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),//tr.data.ptr.buffer是消息体,也就是传输参数Parcel
                tr.data_size,
                reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
                tr.offsets_size/sizeof(size_t), freeBuffer, this);
           
            const pid_t origPid = mCallingPid;
            const uid_t origUid = mCallingUid;
           
            mCallingPid = tr.sender_pid;//客户端进程pid,uid(uid代表什么??)
            mCallingUid = tr.sender_euid;           
           。。。           
            Parcel reply;           
            if (tr.target.ptr) {//tr.target.ptr值在客户端writeTransactionData中并未设置,只是设置了tr.target.handle = handle;猜测在Binder驱动,根据handle找到了对应BBinder的地址并填写到这个字段了。
                sp<BBinder> b((BBinder*)tr.cookie);
                const status_t error = b->transact(tr.code, buffer, &reply, 0);//调用对应的BBinder对象的transact方法
                if (error < NO_ERROR) reply.setError(error);
               
            } else {//tr.target.ptr为空,没有指定BBinder对象,调用the_context_object->transact方法,the_context_object具体是什么对象?我不能够搜索到代码
                const status_t error = the_context_object->transact(tr.code, buffer, &reply, 0);//猜测the_context_object应该是ApplicationThread对象,代表本应用进程上下文,不知道是否正确
                if (error < NO_ERROR) reply.setError(error);
            }
           
            if ((tr.flags & TF_ONE_WAY) == 0) {//默认同步方式,需要发送返回数据
                LOG_ONEWAY("Sending reply to %d!", mCallingPid);
                sendReply(reply, 0);//发送返回数据
            } else {
                LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);//ONEWAY方式客户端并不等待调用返回,因此不需要发送返回数据
            }
           
            mCallingPid = origPid;
            mCallingUid = origUid;

            IF_LOG_TRANSACTIONS() {
                TextOutput::Bundle _b(alog);
                alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj "
                    << tr.target.ptr << ": " << indent << reply << dedent << endl;
            }
           
        }
        break;
   
    case BR_DEAD_BINDER:
     ...
     ...       
   
    }

    ...
    return result;
}
尽管还是存在很多疑问,但是大概脉络应该已经清楚了,收到数据包binder_transaction_data tr后根据tr.target.ptr得到BBinder对象指针,然后调用该对象的transact方法。

9.回到java层的Binder对象
在图一中,对c层的IBinder类继承结构已有一个清楚的说明,BBinder有两个子类,一个是JavaBBinder,一个是 BnInterface,若Binder存根对象用C实现的,那它会继承BnInterface,以MediaPlayerService为例,它的继承 结构如 下:MediaPlayerService-->BnMediaPlayerService-->BnInterface<IMediaPlayerService>-->BBinder-->IBinder
代码调用过程图如下

                                                                   (图3)

这个很简单,再看怎么调用到java层的Binder对象,前面在图1中已经描述 了,java Binder对象对应到C空间的对象是JavaBBinder对象,所以,在BBinder::tansact方法中,调用到得是 JavaBBinder.onTransact方法,在深入JavaBBinder.onTransact前我们先了解一下JavaBBinder是一个 什么对象,它怎么建立与java层Binder对象联系的,下面是JavaBBinder的构造函数:
   //env:java虚拟机指针
  //object:java层的Binder对象
    JavaBBinder(JNIEnv* env, jobject object)
        : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object))
    {
        LOGV("Creating JavaBBinder %p\n", this);
        android_atomic_inc(&gNumLocalRefs);
        incRefsCreated(env);
    }
通过JavaBBinder的构造函数,我们可以推测,在构建java层Binder对象时也构造了对应的C层的一个JavaBBinder,JavaBBinder对象有两个成员,
    JavaVM* const   mVM;
    jobject const   mObject;
显然,JavaBBinder其实就是对java层Binder对象的一个包装对象,理解了这个,我们再看JavaBBinder.onTransact
    virtual status_t onTransact(
        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0)
    {
        JNIEnv* env = javavm_to_jnienv(mVM);
       jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
            code, (int32_t)&data, (int32_t)reply, flags);
        jthrowable excep = env->ExceptionOccurred();
        。。。
        return res != JNI_FALSE ? NO_ERROR : UNKNOWN_TRANSACTION;
    }
我用红色标注了关键代码,可以看到,它通过虚拟机的CallBooleanMethod反向去调用了java层的Binder对象mObject的一个方法,该方法由函数指针gBinderOffsets.mExecTransact引用,我们看一下该函数指针的定义:
    gBinderOffsets.mExecTransact
        = env->GetMethodID(clazz, "execTransact", "(IIII)Z");
总算找到了execTransact方法,总算浮出水面到了我们熟悉的java层,我就不详细解读代码了,画个调用图如下:

 

                                                                                       (图4)

到此,Binder通信的内部机制总算介绍完了,也遗留了不少问题,路过的同仁们若对我提出的这些问题有清楚的也希望分享一下!

posted on 2012-02-08 16:27  小小博客小小员  阅读(910)  评论(0编辑  收藏  举报

导航