Android FrameWork——Binder机制详解
1.前面我曾经发表过一篇blog介绍了aidl实现机制(aidl实现机制浅析),不过那只是停留在java表层,并遗留了一个问题,那就是BinderProxy.transact 该方法本地化实现,同时我指出了它的具体代码位置:
\frameworks\base\core\jni\android_util_Binder.cpp->static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,jint code, jobject dataObj,jobject replyObj, jint flags)
要进一步深入进去,其实涉及了android ipc机制,也就是android独有进程通信机制的Binder,本文主要就是想深入说明Binder机制,
首先说明,这个有点难度,只是对我来说,呵呵,我参加完台湾教授,高焕堂先生开设的android高阶培训课程(android软硬件结合开发培训),并反复看了课件android底层Android框架底层结构知多少?以及网上其它同仁的blog后才有所体会,不过,在些这blog前我还是有不少不解之处,我和大家一样,带着疑问去写这个blog,边写边学习,有问题我会遗留,同时可能有不少不正确的地方,希望路过的高手解答一下,小弟不胜感激。
2.好了,结束开场白,开始Binder机制的探究,就从android_util_Binder.cpp->static jboolean android_os_BinderProxy_transact函数开始,当我们在java层使用java进行进程通信时BinderProxy.transact是本地方法,它jni实现方法是android_os_BinderProxy_transact:
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
jint code, jobject dataObj,
jobject replyObj, jint flags)
{
//把java对象转换成c对象
...
//把java层的obj(BinderProxy)对象转换成C层的BpBinder对象
IBinder* target = (IBinder*)
env->GetIntField(obj, gBinderProxyOffsets.mObject);
status_t err = target->transact(code, *data, reply, flags);//BpBinder->transact
...
}
我只写出了关键代码,具体的你可以认真阅读相关代码,其实该函数主要做的工作就是把java对象转成c++对象,然后调用BpBinder->transact方法。
3.BinderProxy,BpBinder等这些对象你看了可能有些晕呼呼,这些是什么呢,在进一步深入研究前先画一个类静态结构图描述一下这些类之间的相关关系:
(图1)
--1.在java层,有一个IBinder接口,这个在读这个blog前你应该已经很熟悉了,它有两个实现类,一个是BinderProxy,是用作客户端的,一个是Binder用作服务端的存根类
--2.类似在C++层,也有一个IBinder接口,同样它也有两个子类,BpBinder用作客户端代理类,BBinber用作服务端存根类
--3.从上图可以很清晰的看出我在段落2中写的BinderProxy.transact调用轨迹,BinderProxy.transact()-->android_util_Binder.cpp(android_os_BiinderProxy_transact())-->BpBinder.transact()
4.接下来我们进入BpBinder.transact()研究一下:
//code:指定调用哪个函数的code常量
//data:java Parcel Object to c++ Parcel Object,不过这里作为引用传递参数防止修改data指针(C++大学学过,工作中一直从事java开发,若有误,请提出)
//reply:返回数据Parcel对象指针
//flags:0:normal调用,同步方式,1:异步调用,发送数据结束后不等待调用结果返回
status_t BpBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
// Once a binder has died, it will never come back to life.
if (mAlive) {
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}
////////////////////////////////////////////////////////////////////////////
/*BpBinder::transact-->*/
//handle:==BpBinder.mHandle,它在BpBinder构建的时候被赋值,它代表BpBinder所连接的BBinder所在的地址
//其它参数见上文说明
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
status_t err = data.errorCheck();//校验传入参数
flags |= TF_ACCEPT_FDS;
IF_LOG_TRANSACTIONS() {
TextOutput::Bundle _b(alog);
alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand "
<< handle << " / code " << TypeCode(code) << ": "
<< indent << data << dedent << endl;
}
if (err == NO_ERROR) {
LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
(flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);//传输数据
}
if (err != NO_ERROR) {
if (reply) reply->setError(err);//校验或者传输数据出错,设置返回数据错误信息
return (mLastError = err);
}
if ((flags & TF_ONE_WAY) == 0) {
if (reply) {
err = waitForResponse(reply);//等待返回数据
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
IF_LOG_TRANSACTIONS() {
TextOutput::Bundle _b(alog);
alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand "
<< handle << ": ";
if (reply) alog << indent << *reply << dedent << endl;
else alog << "(none requested)" << endl;
}
} else {
err = waitForResponse(NULL, NULL);
}
return err;
}
我再对代码进行一下梳理,BpBinder.transact()方法很简单,它调用了IPCThreadState::self()->transact(mHandle, code, data, reply, flags);就结束了,并没有做什么事情,不过增加了一个参数,mHandle,它在BpBinder被构建的时候创建的,而具体mHandle值是怎么得来的,涉及到serviceManager框架,我这里先保留这个问题,在后面我弄清楚serviceManager框架后再对该问题进行说明。
然后再看下IPCThreadState::transact的实现,我再代码中作出了简单的注释,详细的代码逻辑我也没有搞太清楚,阅读C++代码,我能力还是有待提高,我关注两个关键函数调用,一个是传输数据,另一个是等待数据返回,我在函数中已经把它标注成为红色了。
--cmd==BC_TRANSACTION
--binderFlags==处理后的flags, flags |= TF_ACCEPT_FDS;
--handle BpBinder句柄
--data 传输数据引用
--statusBuffer==null
status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
binder_transaction_data tr;//构造传输数据包
tr.target.handle = handle;//设置目标句柄
tr.code = code;//设置目标函数code
tr.flags = binderFlags;//设置调用方式,是否等待返回
const status_t err = data.errorCheck();//传输参数数据校验
if (err == NO_ERROR) {
tr.data_size = data.ipcDataSize();//设置消息体长度
tr.data.ptr.buffer = data.ipcData();//设置消息体
tr.offsets_size = data.ipcObjectsCount()*sizeof(size_t);//??
tr.data.ptr.offsets = data.ipcObjects();
} else if (statusBuffer) {
tr.flags |= TF_STATUS_CODE;
*statusBuffer = err;
tr.data_size = sizeof(status_t);
tr.data.ptr.buffer = statusBuffer;
tr.offsets_size = 0;
tr.data.ptr.offsets = NULL;
} else {
return (mLastError = err);
}
mOut.writeInt32(cmd);//写入命令
mOut.write(&tr, sizeof(tr));//写入数据
return NO_ERROR;
}
这里已经可以看到了IPC底层通信的一些轨迹了,不过遗憾我还没有弄懂mOut是怎么被赋值,如何与Binder驱动建立联系的,若路过的高手知道的话,希望能够不吝赐教
--waitForResponse
//reply返回数据的Parcel指针
//acquireResult:??
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
int32_t cmd;//返回消息命令
int32_t err;
while (1) {//消息接收循环
if ((err=talkWithDriver()) < NO_ERROR) break;//跟Binder Driver通信,看是否有要接收数据
err = mIn.errorCheck();
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue;
cmd = mIn.readInt32();//获取消息命令
...
//BR开头的cmd都是返回数据,否则就应该是当该进程作为服务端的时候接收来自客户端的命令
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
...
case BR_DEAD_REPLY:
...
...
case BR_REPLY://猜测这表示有返回数据的命令
{
binder_transaction_data tr;
err = mIn.read(&tr, sizeof(tr));
LOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
if (err != NO_ERROR) goto finish;
if (reply) {
if ((tr.flags & TF_STATUS_CODE) == 0) {
reply->ipcSetDataReference(//设置返回数据
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(size_t),
freeBuffer, this);
} else {
err = *static_cast<const status_t*>(tr.data.ptr.buffer);
freeBuffer(NULL,
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(size_t), this);
}
} else {
freeBuffer(NULL,
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(size_t), this);
continue;
}
}
goto finish;
default://不是返回数据,作为服务端接收来自客户端的命令??
err = executeCommand(cmd);
if (err != NO_ERROR) goto finish;
break;
}
}
finish:
if (err != NO_ERROR) {
if (acquireResult) *acquireResult = err;
if (reply) reply->setError(err);
mLastError = err;
}
return err;
}
为了保证文章篇幅,我可能剪裁了一些不是很相关代码,我并不打算细致了解waitForResponse,我只是要知道它是如何接收并设置返回数据的,mIn与mOut一样,我同样不知道它是怎么构建的,我暂且把它理解成Binder驱动设备的输入输出流,这样理解应该是没有问题的,待有进一步的学习了解,我再作说明
5.IPCThreadState* IPCThreadState::self()
为什么我这里要讨论 IPCThreadState::self函数呢,如果你是带着思考来看我的blog的话你可能已经发现了IPCThreadState::transact函数是有点问题的,什么问题呢?看我在代码中标注为红色的传输数据和等待返回数据的两个函数调用,这样的同步调用显然是需要同步锁的,不然的话多线程执行的话就会有问题的,可能我等待回来的数据是另一个binder调用返回的结果,而一个客户端显然是容许多个线程调用多个binder的,而 IPCThreadState::self()似乎跟java中的单例对象很像,这样的话所有线程都调用transact而又不加同步锁的话是有问题的,我后面仔细看了好几回代码,里外都没有加锁的代码,最后发现问题在IPCThreadState::self():
IPCThreadState* IPCThreadState::self()
{
if (gHaveTLS) {//gHaveTLS:猜测表示IPCThreadState是否已经存在
restart:
const pthread_key_t k = gTLS;
IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);//获取当前线程绑定的IPCThreadState
if (st) return st;
return new IPCThreadState;
}
if (gShutdown) return NULL;
pthread_mutex_lock(&gTLSMutex);
if (!gHaveTLS) {//IPCThreadState 没有绑定到当前线程
if (pthread_key_create(&gTLS, threadDestructor) != 0) {//创建并一个IPCThreadState到当前线程,并把引用地址回填gTLS
pthread_mutex_unlock(&gTLSMutex);//绑定失败,释放锁,并返回null
return NULL;
}
gHaveTLS = true;//绑定成功,修改标志变量
}
pthread_mutex_unlock(&gTLSMutex);//释放锁
goto restart;
}
我不知道我上面的注释是否正确,涉及到系统函数调用,我理解不一定正确,如你是C开发者,请帮我指正,谢谢!
从我的注释,你应该已经理解了,self函数返回的是线程绑定对象,而非是单例对象,这个跟数据库事务线程绑定应该是类似的,看完这个我就恍然大悟,原来IPCThreadState是线程持有的,也就是每个线程拥有一个对象,这样的话它
调用writeTransactionData写完数据后调用waitForResponse等待返回数据是没有问题的,就像我原来写ServerSocket的Socket连接池一样,每个Socket连接new一个线程进行通信一样,没有问题的。
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通信的内部机制总算介绍完了,也遗留了不少问题,路过的同仁们若对我提出的这些问题有清楚的也希望分享一下!