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