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類別和物件:
(图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通信的内部机制总算介绍完了,也遗留了不少问题,路过的同仁们若对我提出的这些问题有清楚的也希望分享一下!