在之前一篇中简单的介绍了一下setDataSource的流程,其中遇到了一个新的消息机制AHandler,其实这东西本来不需要介绍,因为消息机制原本就是一个很成熟和常见的技术技巧,这玩意里面包含了计算机哲学和计算机玄学的双学位问题,听起来牛逼轰轰,其实也就那回事了。为了这次文档的完整性,再一个后面可能要整理到公司的文档库中,所以在此介绍一下,熟悉的同学直接飘过了。
先说说为什么要用消息机制,在我们的软件开发中经常会遇到多线程的问题,由此便带来了线程同步的问题,比如各种锁,最常见的就是Mutex,而线程同步通常是有代价的,这个代价一方面来自于锁本身,而另一方面来自于程序设计,在一个有众多线程的进程中使用同步锁常常造成各种死锁问题,当然你可以说你水平高超没有这个问题,而通常解决锁之间的竞争往往在效率上也要付出相应的代价,当然了你也可以说你连这也能解决,那我也只能膜拜了。然而通常还有另一个问题,便是线程间的执行顺序问题,如果使用锁和条件变量来控制,在线程众多的情况下,可控性只会越来越差,另外通常来讲CPU的利用率也不高。那么消息机制是如何来解决这些问题的呢?
第一个问题是什么是消息机制,这里先介绍一个同步/异步的的概念,举例来说明,
比如说肚子饿了去饭店吃饭,点好菜后你坐那老老实实等着服务员上菜,什么时候菜做了好你才能结束等待,那这就是同步。
而异步就不用干等了,掏出手机,打开外卖软件,下单,OK,这时你就可以去干别的事情了,应该没有人死盯着手机干等吧,到时候饭店做好饭,小哥自然送上门。
听起来异步比同步是先进很多了,但在现实中事情往往没有想象的那么容易,首先你用外卖软件,如果我是老板,餐厅已经爆满了,顾客在饭店里已经骂娘了,哪还顾得上你,第二你点完得等送餐吧,又多了一段路程,而同步也有自己的缺点,坐在餐馆等饭的时间对于你而言算是浪费了,比如你还有其他事情没做此时也只能乖乖干坐着了。
好吧,扯太远了,回到消息机制上来。
当我们想要我们的程序执行一些事情的时候要么同步,要么异步,而消息机制便是一种异步的方式,首先创建一个消息队列,这个消息队列在一个单独的线程中轮询,一旦有人发送消息给它,它就会将消息取出执行,执行后又回到等待状态直到有新的消息到来。
如下图:
消息被投递到一个消息队列中去,而Looper会持续不断的从Message Queue中拿出消息,再投递给合适的Handler去处理。
好了,开始对Android的这个消息队列进行分析吧,依旧从使用上入手,通常在使用的开始会创建一个ALooper对象,然后会实现一个AHandler的子类,最后创建消息进行投递进入Message Queue。
首先我们先看一下AHandler的定义。代码不多直接贴上来。
namespace android {
struct AMessage;
struct AHandler : public RefBase {
AHandler()
: mID(0),
mVerboseStats(false),
mMessageCounter(0) {
}
ALooper::handler_id id() const {
return mID;
}
sp<ALooper> looper() const {
return mLooper.promote();
}
wp<ALooper> getLooper() const {
return mLooper;
}
wp<AHandler> getHandler() const {
// allow getting a weak reference to a const handler
return const_cast<AHandler *>(this);
}
protected:
virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
private:
friend struct AMessage; // deliverMessage()
friend struct ALooperRoster; // setID()
ALooper::handler_id mID;
wp<ALooper> mLooper;
inline void setID(ALooper::handler_id id, wp<ALooper> looper) {
mID = id;
mLooper = looper;
}
bool mVerboseStats;
uint32_t mMessageCounter;
KeyedVector<uint32_t, uint32_t> mMessages;
void deliverMessage(const sp<AMessage> &msg);
DISALLOW_EVIL_CONSTRUCTORS(AHandler);
};
} // namespace android
cpp文件:
namespace android {
void AHandler::deliverMessage(const sp<AMessage> &msg) {
onMessageReceived(msg);
mMessageCounter++;
if (mVerboseStats) {
uint32_t what = msg->what();
ssize_t idx = mMessages.indexOfKey(what);
if (idx < 0) {
mMessages.add(what, 1);
} else {
mMessages.editValueAt(idx)++;
}
}
}
} // namespace android
由于该类为基类,在使用时需要自己定义子类去继承重新类中的方法,所先以该类的Cpp文件相对比较简单,只定义了一个deliverMessage方法,
先看看.h文件中该类的构成,其中有一个容易混淆的成员mMessages被定义为KeyedVector类型,该并不是真正的Message Queue,相反该对象并没有什么太大的实际作用,只有在verbose模式下才存储一个消息被传递的次数,对我们分析没什么帮助。剩下的就是一个id值,和一个ALooper的弱引用。刚刚我们已经说过了,Looper负责将消息源源不断的发送给Handler来处理,所以两者是必然有关联的。另外还有一个mVerboseStats的bool值,这玩意实际上没啥用,先不看了。之后看看一个重要的纯虚函数onMessageReceived,我们定义的子类最重要的就是实现这个方法。之后会通过调用把Message发给这个函数,而这个函数会根据这个Message中的what值来判断需要进行什么操作,习惯用法是通过一个switch来处理。
实际的Media代码中这个AHandler的使用非常之多,只需简单搜索便可以找到很多的例子来查看。 现在应该来看看ALooper这个类了,这个类的作用非常明确,在创建时会开启一个线程,不断的对消息队列进行检查,如果一旦有消息便拿出来给相应的AHandler处理。比较熟悉的人应该可以想到这种情景下需要一个Condition条件变量来控制循环,否则持续的轮询会给系统产生可怕的负担。 代码都不多直接全放上来好了,先看.h文件
namespace android {.cpp文件:
struct AHandler;
struct AMessage;
struct AReplyToken;
struct ALooper : public RefBase {
typedef int32_t event_id;
typedef int32_t handler_id;
ALooper();
// Takes effect in a subsequent call to start().
void setName(const char *name);
handler_id registerHandler(const sp<AHandler> &handler);
void unregisterHandler(handler_id handlerID);
status_t start(
bool runOnCallingThread = false,
bool canCallJava = false,
int32_t priority = PRIORITY_DEFAULT
);
status_t stop();
static int64_t GetNowUs();
const char *getName() const {
return mName.c_str();
}
protected:
virtual ~ALooper();
private:
friend struct AMessage; // post()
struct Event {
int64_t mWhenUs;
sp<AMessage> mMessage;
};
Mutex mLock;
Condition mQueueChangedCondition;
AString mName;
List<Event> mEventQueue;
struct LooperThread;
sp<LooperThread> mThread;
bool mRunningLocally;
// use a separate lock for reply handling, as it is always on another thread
// use a central lock, however, to avoid creating a mutex for each reply
Mutex mRepliesLock;
Condition mRepliesCondition;
// START --- methods used only by AMessage
// posts a message on this looper with the given timeout
void post(const sp<AMessage> &msg, int64_t delayUs);
// creates a reply token to be used with this looper
sp<AReplyToken> createReplyToken();
// waits for a response for the reply token. If status is OK, the response
// is stored into the supplied variable. Otherwise, it is unchanged.
status_t awaitResponse(const sp<AReplyToken> &replyToken, sp<AMessage> *response);
// posts a reply for a reply token. If the reply could be successfully posted,
// it returns OK. Otherwise, it returns an error value.
status_t postReply(const sp<AReplyToken> &replyToken, const sp<AMessage> &msg);
// END --- methods used only by AMessage
bool loop();
DISALLOW_EVIL_CONSTRUCTORS(ALooper);
};
} // namespace android
namespace android {
ALooperRoster gLooperRoster;
struct ALooper::LooperThread : public Thread {
LooperThread(ALooper *looper, bool canCallJava)
: Thread(canCallJava),
mLooper(looper),
mThreadId(NULL) {
}
virtual status_t readyToRun() {
mThreadId = androidGetThreadId();
return Thread::readyToRun();
}
virtual bool threadLoop() {
return mLooper->loop();
}
bool isCurrentThread() const {
return mThreadId == androidGetThreadId();
}
protected:
virtual ~LooperThread() {}
private:
ALooper *mLooper;
android_thread_id_t mThreadId;
DISALLOW_EVIL_CONSTRUCTORS(LooperThread);
};
// static
int64_t ALooper::GetNowUs() {
return systemTime(SYSTEM_TIME_MONOTONIC) / 1000ll;
}
ALooper::ALooper()
: mRunningLocally(false) {
// clean up stale AHandlers. Doing it here instead of in the destructor avoids
// the side effect of objects being deleted from the unregister function recursively.
gLooperRoster.unregisterStaleHandlers();
}
ALooper::~ALooper() {
stop();
// stale AHandlers are now cleaned up in the constructor of the next ALooper to come along
}
void ALooper::setName(const char *name) {
mName = name;
}
ALooper::handler_id ALooper::registerHandler(const sp<AHandler> &handler) {
return gLooperRoster.registerHandler(this, handler);
}
void ALooper::unregisterHandler(handler_id handlerID) {
gLooperRoster.unregisterHandler(handlerID);
}
status_t ALooper::start(
bool runOnCallingThread, bool canCallJava, int32_t priority) {
if (runOnCallingThread) {
{
Mutex::Autolock autoLock(mLock);
if (mThread != NULL || mRunningLocally) {
return INVALID_OPERATION;
}
mRunningLocally = true;
}
do {
} while (loop());
return OK;
}
Mutex::Autolock autoLock(mLock);
if (mThread != NULL || mRunningLocally) {
return INVALID_OPERATION;
}
mThread = new LooperThread(this, canCallJava);
status_t err = mThread->run(
mName.empty() ? "ALooper" : mName.c_str(), priority);
if (err != OK) {
mThread.clear();
}
return err;
}
status_t ALooper::stop() {
sp<LooperThread> thread;
bool runningLocally;
{
Mutex::Autolock autoLock(mLock);
thread = mThread;
runningLocally = mRunningLocally;
mThread.clear();
mRunningLocally = false;
}
if (thread == NULL && !runningLocally) {
return INVALID_OPERATION;
}
if (thread != NULL) {
thread->requestExit();
}
mQueueChangedCondition.signal();
{
Mutex::Autolock autoLock(mRepliesLock);
mRepliesCondition.broadcast();
}
if (!runningLocally && !thread->isCurrentThread()) {
// If not running locally and this thread _is_ the looper thread,
// the loop() function will return and never be called again.
thread->requestExitAndWait();
}
return OK;
}
void ALooper::post(const sp<AMessage> &msg, int64_t delayUs) {
Mutex::Autolock autoLock(mLock);
int64_t whenUs;
if (delayUs > 0) {
whenUs = GetNowUs() + delayUs;
} else {
whenUs = GetNowUs();
}
List<Event>::iterator it = mEventQueue.begin();
while (it != mEventQueue.end() && (*it).mWhenUs <= whenUs) {
++it;
}
Event event;
event.mWhenUs = whenUs;
event.mMessage = msg;
if (it == mEventQueue.begin()) {
mQueueChangedCondition.signal();
}
mEventQueue.insert(it, event);
}
bool ALooper::loop() {
Event event;
{
Mutex::Autolock autoLock(mLock);
if (mThread == NULL && !mRunningLocally) {
return false;
}
if (mEventQueue.empty()) {
mQueueChangedCondition.wait(mLock);
return true;
}
int64_t whenUs = (*mEventQueue.begin()).mWhenUs;
int64_t nowUs = GetNowUs();
if (whenUs > nowUs) {
int64_t delayUs = whenUs - nowUs;
mQueueChangedCondition.waitRelative(mLock, delayUs * 1000ll);
return true;
}
event = *mEventQueue.begin();
mEventQueue.erase(mEventQueue.begin());
}
event.mMessage->deliver();
// NOTE: It's important to note that at this point our "ALooper" object
// may no longer exist (its final reference may have gone away while
// delivering the message). We have made sure, however, that loop()
// won't be called again.
return true;
}
// to be called by AMessage::postAndAwaitResponse only
sp<AReplyToken> ALooper::createReplyToken() {
return new AReplyToken(this);
}
// to be called by AMessage::postAndAwaitResponse only
status_t ALooper::awaitResponse(const sp<AReplyToken> &replyToken, sp<AMessage> *response) {
// return status in case we want to handle an interrupted wait
Mutex::Autolock autoLock(mRepliesLock);
CHECK(replyToken != NULL);
while (!replyToken->retrieveReply(response)) {
{
Mutex::Autolock autoLock(mLock);
if (mThread == NULL) {
return -ENOENT;
}
}
mRepliesCondition.wait(mRepliesLock);
}
return OK;
}
status_t ALooper::postReply(const sp<AReplyToken> &replyToken, const sp<AMessage> &reply) {
Mutex::Autolock autoLock(mRepliesLock);
status_t err = replyToken->setReply(reply);
if (err == OK) {
mRepliesCondition.broadcast();
}
return err;
}
} // namespace android
首先看一下cpp文件开始变声明了一个LooperThread的内部类型,该类型继承与Thread类。通过构造函数保存了ALooper指针,这些没什么可说的,看一下threadLoop函数直接调用了ALooper的loop函数,果然和我们之前描述的一致,该线程不断的调用loop函数,而loop函数自身则是对消息队列的查询,如此,整个消息队列便在Looper的驱动下活动起来了。成员对象List<Event> mEventQueue;便是我们要寻找的Message Queue了,其他的接口主要有setName给Looper起个名字,这个名字其实蛮重要的,因为最后会用作LooperThread的名字,线程的名字是一个很重要的信息,start函数就无需多言了创建LooperThread然后调用run函数把线程跑起来,就可以了,而这个线程便会不断调用ALooper的loop函数来驱动消息队列。 再来看一下ALooper的成员变量。
Mutex mLock;首先是两对搭配使用的Mutex和Condition,mQueueChangedCondition是用来阻塞loop函数的,如果没有它loop函数会在消息队列中没有Event时继续空转,那CPU就耗光了,而mRepliesCondition则是用来做消息的同步等待回复的,后续碰到再说吧。 剩下的几个函数,我们先说简单常用的,post方法,该方法是将消息投递到队列中,第一个参数是AMessage也就是消息,第二个参数是延迟时间,实现非常简单,创建一个Event然后将其加入队列即可,另外就是如果消息队列原本为空就通过mQueueChangedCondition.signal();唤醒队列的线程。 其中的内部类型Event就不多说了,对message的简单包装,剩下的createReplyToken/awaitResponse/postReply是用来发同步消息等待反馈的,我们一会再说。 再看看loop方法的具体细节,首先会判断如果mThread==NULL并且mRunningLocally==false则函数返回false,如果返回了false,线程就不会在执行下一次的loop函数了。接下来判断mEventQueue是否为空,也就是判断消息队列中是否有消息,如果没有就使用mQueueChangedCondition.wait使线程阻塞,直到刚才介绍post时说的那样被唤醒。剩下的如果是队列中第一个要执行的消息有延迟时间,那么就是用Condition的waitRelative方法阻塞等待。最后一切都没问题了就准备处理消息了,先把消息从队列删除,之后通过调用event.mMessage->deliver()方法来处理消息。处理的细节我们一会介绍AMessage时再来看。
Condition mQueueChangedCondition;
Mutex mRepliesLock;
Condition mRepliesCondition;
前面说了ALooper和AHandler相对都是比较简单的,剩下一个AMessage反倒是这个消息队列中比较复杂的一个组成部分了,代码稍微有点多,所以还是分段来看。 首先说一点,这个Message不仅仅是传递消息这么简单,它可以附带上很多的键值对在身上,也就是说这个Message在传递的时候除了最重要的what值之外还能带上一些其他的值,而每个值都有一个对应的key,这样接收方在处理消息时可以根据这个key值拿出在Message中存储的value,这样一次可以传递的信息就大大增加了。 在AMessage的定义中有如下的一个内部类的定义
struct Item {
union {
int32_t int32Value;
int64_t int64Value;
size_t sizeValue;
float floatValue;
double doubleValue;
void *ptrValue;
RefBase *refValue;
AString *stringValue;
Rect rectValue;
} u;
const char *mName;
size_t mNameLength;
Type mType;
void setName(const char *name, size_t len);
};
这个类型所定义的就是一个key-value,被命名为Item, 其中使用联合体这段空间来存储value,使用mName来作为key。mNameLength来存储key值得长度,这个长度在这里还挺重要的,后面我们再看,mType是一个枚举值,保存了所存数据是什么类型的,枚举体定义如下:
enum Type {之后AMessage定义了一个数组Item mItems[kMaxNumItems];保存所有的Item不过这样确定不浪费空间吗?另外便定义了一堆的find和set函数,都是用来设置这些键值对的。一个AMessage最多携带kMaxNumItems个Item,默认为64. 除了这个之外还有一个类AReplyToken,这个虽然不是AMessage的内部类,但我猜想这个类不作为内部类的原因更多的可能是因为C++的一个语法限制。这个类的作用我们等会再说,先看看AMessage的另外两个成员变量
kTypeInt32,
kTypeInt64,
kTypeSize,
kTypeFloat,
kTypeDouble,
kTypePointer,
kTypeString,
kTypeObject,
kTypeMessage,
kTypeRect,
kTypeBuffer,
};
wp<AHandler> mHandler;这两个猜都知道啦,一个是处理该消息的Handler一个是处理消息队列轮询的Looper。 当然AMessage中少不了的还有个what值,定义如下:
wp<ALooper> mLooper;
uint32_t mWhat;因为一个Handler不是给一个Message专用的,可能会处理很多种的消息,那么为了区分便加入一个what值来告诉Handler现在的消息是哪种,一般Handler会使用一个switch来进行处理,这个源码中例子多的不要不要的,自己看吧。
好啦,看看构造函数先:
AMessage();一般使用第二种了,传入消息的what值和handler,mNumItems赋0表示目前还没有携带的附加信息,也就是键值对Item啦。之后调用setTarget
AMessage(uint32_t what, const sp<const AHandler> &handler);
AMessage::AMessage(void)
: mWhat(0),
mTarget(0),
mNumItems(0) {
}
AMessage::AMessage(uint32_t what, const sp<const AHandler> &handler)
: mWhat(what),
mNumItems(0) {
setTarget(handler);
}
void AMessage::setTarget(const sp<const AHandler> &handler) {mTarge是handler的一个id值,这个id是累加生成的,回头再说吧,暂时用不到,剩下的就没什么,简单赋值。另外为啥要调用handler的getHandler的方法而不直接赋值呢?主要是因为有可能AHandler是一个const的指针,getHandler方法会将其通过const_cast转换,那为啥要搞成非const的呢,因为要用wp,那为啥用wp就要非const呢。。。不想回答你。。。 好吧,看看AMessage的post方法吧,这是我们要分析的重点,
if (handler == NULL) {
mTarget = 0;
mHandler.clear();
mLooper.clear();
} else {
mTarget = handler->id();
mHandler = handler->getHandler();
mLooper = handler->getLooper();
}
}
status_t AMessage::post(int64_t delayUs) {实现挺简单的吧,直接调用ALooper的post方法。ALooper的post我们已经说过了加入到消息队列就没了。之后loop函数会把消息取出来调用Message的deliver方法,我们来看看这个deliver方法是干嘛的。
sp<ALooper> looper = mLooper.promote();
if (looper == NULL) {
ALOGW("failed to post message as target looper for handler %d is gone.", mTarget);
return -ENOENT;
}
looper->post(this, delayUs);
return OK;
}
void AMessage::deliver() {直接拿出handler调用了deliverMessage方法,这个方法我们刚才说过了。会不会想为啥AMessage的post方法不直接调用deliver方法呢,如果有这个问题证明你压根没看懂,再看一遍吧,当ALooper通过loop函数拿出消息调用deliver的时候,这个deliver才会在Looper的线程里面执行,而如果直接调用那就成了当前线程同步调用了。
sp<AHandler> handler = mHandler.promote();
if (handler == NULL) {
ALOGW("failed to deliver message as target handler %d is gone.", mTarget);
return;
}
handler->deliverMessage(this);
}
好,我们看最后一个问题,AMessage中的 postAndAwaitResponse方法,这个方法也是用来发送消息的,不同之处在于这个方法发送完消息以后会等待接收方返回一个答复,所以你懂的,main thread里面肾用。
好,此时我们需要看看刚才提了一下的
struct AReplyToken : public RefBase {这个类先放着,到底干嘛用的一会再说。
AReplyToken(const sp<ALooper> &looper)
: mLooper(looper),
mReplied(false) {
}
private:
friend struct AMessage;
friend struct ALooper;
wp<ALooper> mLooper;
sp<AMessage> mReply;
bool mReplied;
sp<ALooper> getLooper() const {
return mLooper.promote();
}
// if reply is not set, returns false; otherwise, it retrieves the reply and returns true
bool retrieveReply(sp<AMessage> *reply) {
if (mReplied) {
*reply = mReply;
mReply.clear();
}
return mReplied;
}
// sets the reply for this token. returns OK or error
status_t setReply(const sp<AMessage> &reply);
};
我们还是从源头上来看,这种情形通常是从AMessage的对象调用postAndAwaitResponse开始的,这个函数有一个参数是sp<AMessage>*类型了,sp包含的已经是指针了,那这里显然是一个out型的参数,也就是在调用结束后这个参数会被赋值。我们先看看这个函数的定义。
status_t AMessage::postAndAwaitResponse(sp<AMessage> *response) {首先拿到looper调用createReplyToken创建了一个token,这点我们一会来看,紧接着looper调用post把消息post出去了。最后调用了looper的awaitResponse方法,这个方法的代码前面已经给出,我们这里再单独拿出来看看
sp<ALooper> looper = mLooper.promote();
if (looper == NULL) {
ALOGW("failed to post message as target looper for handler %d is gone.", mTarget);
return -ENOENT;
}
sp<AReplyToken> token = looper->createReplyToken();
if (token == NULL) {
ALOGE("failed to create reply token");
return -ENOMEM;
}
setObject("replyID", token);
looper->post(this, 0 /* delayUs */);
return looper->awaitResponse(token, response);
}
// to be called by AMessage::postAndAwaitResponse only很明显该函数会在mRepliesCondition这个条件变量上等待,直到条件变成replyToken->retrieveReply返回ture。通过上面的代码可以知道这里的replyToken就是刚才从AMessage传过来的,但是有时这个Looper自己调用createReplyToken创建的,而这个创建不过就是调用了AReplyToken的构造函数,然后把Looper自己的指针传了过去。这到底有啥关联呢,我们先不着急看这个,先看看mRepliesCondition这个条件变量在何时才能signal或者broadcast,找到突破口,一共有两处一处是postReply方法,一处是stop方法,不用看也知道stop是万万不行滴。那么就只能是这个postReply了,代码如下:
status_t ALooper::awaitResponse(const sp<AReplyToken> &replyToken, sp<AMessage> *response) {
// return status in case we want to handle an interrupted wait
Mutex::Autolock autoLock(mRepliesLock);
CHECK(replyToken != NULL);
while (!replyToken->retrieveReply(response)) {
{
Mutex::Autolock autoLock(mLock);
if (mThread == NULL) {
return -ENOENT;
}
}
mRepliesCondition.wait(mRepliesLock);
}
return OK;
}
status_t ALooper::postReply(const sp<AReplyToken> &replyToken, const sp<AMessage> &reply) {处理唤醒broadcast调用外,还调用了replyToken的setReply方法,先看看这个setReply,前面已经给出过代码了,这里重复一下:
Mutex::Autolock autoLock(mRepliesLock);
status_t err = replyToken->setReply(reply);
if (err == OK) {
mRepliesCondition.broadcast();
}
return err;
}
status_t AReplyToken::setReply(const sp<AMessage> &reply) {做了两件事情,给成员mReply赋值为参数,mReplied赋值为ture,好像看不出来啥用。我们接着看看上面的postReply既然broadcast已经发了,那么之前在ALooper::awaitResponse函数中while循环也得跳出吧,现在是时候看看这个while中的retrieveReply返回啥玩意了。
if (mReplied) {
ALOGE("trying to post a duplicate reply");
return -EBUSY;
}
CHECK(mReply == NULL);
mReply = reply;
mReplied = true;
return OK;
}
bool retrieveReply(sp<AMessage> *reply) {直接看返回语句,这个返回的值就是mReplied,也就是说如果mReplied为ture这事就算了了,这样就和前面的setReply对上了,只要调用setReply的这个AReplyToken和postReply参数里的那个token是同一个,这个条件变量就可以被唤醒了。那么问题是,谁来做这个postReply的发起人呢,token又从哪来,那么对外的接口而言,AMessage是首当其冲的,所以从它开始找,一找就找到了。AMessage有一个同名函数也叫postReply,刚好就调用了ALooper的postReply函数,我们来仔细看看。
if (mReplied) {
*reply = mReply;
mReply.clear();
}
return mReplied;
}
// sets the reply for this token. returns OK or error
status_t setReply(const sp<AMessage> &reply);
status_t AMessage::postReply(const sp<AReplyToken> &replyToken) {调用很简单,问题还是没变token从哪来。我们回想一下刚才在最初调用postAndAwaitResponse中,通过looper的createReplyToken函数创建了一个token,AMessage还通过setObject("replyID", token);这句把它设置为自己的一个item字段,这样我们想在唤醒时再次取到这个token当然要通过AMessage变量来了。直接告知答案了,AMessage有一个叫senderAwaitsResponse的方法,就是干这事的。
if (replyToken == NULL) {
ALOGW("failed to post reply to a NULL token");
return -ENOENT;
}
sp<ALooper> looper = replyToken->getLooper();
if (looper == NULL) {
ALOGW("failed to post reply as target looper is gone.");
return -ENOENT;
}
return looper->postReply(replyToken, this);
}
bool AMessage::senderAwaitsResponse(sp<AReplyToken> *replyToken) {
sp<RefBase> tmp;
bool found = findObject("replyID", &tmp);
if (!found) {
return false;
}
*replyToken = static_cast<AReplyToken *>(tmp.get());
tmp.clear();
setObject("replyID", tmp);
// TODO: delete Object instead of setting it to NULL
return *replyToken != NULL;
}
代码中的参数replyToken是一个sp的指针,也就表示在函数中会对这个sp赋值,所以我们只需要声明一个sp<AReplyToken>对象,然后传入它的指针即可。 这样我们便拿到了这个token,那由谁来发送呢?答案是谁都可以,我们回想一下,AMessage的postReply方法是从参数token中拿到looper然后一路调用下去的,跟之前的AMessage可是没一点关系了。所以只需创建一个新的AMessage就行了。 一般的习惯用法,给出一个伪代码的参考:
sp<AMessage> msg = new AMessage(what, handler); sp<AMessage> response; msg->postAndAwaitResponse(response);
接收的Handler在处理完消息后发送答复:
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
sp<AMessage> response = new AMessage;
response->postReply(replyID);
这样整个await的消息就完成了。
文中有一点没有介绍,那就是ALooper有一个registerHandler的方法,这个方法除了调用AHandler的setID将Looper和Handler关联起来之外,其中还引出了一个LooperRoster的类型,而且是一个全局对象,那么这个对象有什么用,这个registerHandler方法不调用行不行,留给各位自己研究啦。