1.AReplyToken源码
下面是安卓N版本AReplyToken源码。
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);
};
AReplyToken有一个成员sp< AMessage > mReply,这个是用来存放应答消息的引用的。其中辅助判断是否被设置了应答消息的布尔变量bool mReplied,这个值在构造函数被初始化为false。
还一个辅助使用的函数setReply,源码如下:
status_t AReplyToken::setReply(const sp<AMessage> &reply) {
if (mReplied) {
ALOGE("trying to post a duplicate reply");
return -EBUSY;
}
CHECK(mReply == NULL);
mReply = reply;
mReplied = true;
return OK;
}
一旦通过AReplyToken::setReply函数对应答消息mReply完成设置以后就将辅助布尔变量设置为true。
与此向对应的还有一个AReplyToken提供的函数retrieveReply,是向外部提供的接口用来返回被设置的应答消息。源码如下:
bool retrieveReply(sp<AMessage> *reply) {
if (mReplied) {
*reply = mReply;
mReply.clear();
}
return mReplied;
}
通过上面的分析知道AReplyToken向外部提供了一个函数接口retrieveReply(sp< AMessage> *reply),既然是一个阻塞的消息机制,AReplyToken又是这一机制的桥梁。很显然就是等待应答消息完成设置的一方循环调用retrieveReply(sp< AMessage> *reply)函数,直到得到应答消息的引用并返回值位true为止,当返回值位true的时候就说明通过retrieveReply函数接口得到了在AReplyToken里设置的应答消息的引用。
而对携带AReplyToken对象引用的AMessage消息对象进行处理的一方,需要检查是否含有AReplyToken对象的引用,如果有,就准备好应答消息,然后通过AReplyToken提供的外部接口setReply设置应答消息。
对于等待应答消息完成设置的一方的形式是这样的:
while (!replyToken->retrieveReply(response)) {
}
这样就循环等待,直到应答消息的到来。
2.阻塞消息发送
下面贴出一个例子:
==>
status_t NuPlayer::getSelectedTrack(int32_t type, Parcel* reply) const {
sp<AMessage> msg = new AMessage(kWhatGetSelectedTrack, this);
msg->setPointer("reply", reply);
msg->setInt32("type", type);
sp<AMessage> response;
status_t err = msg->postAndAwaitResponse(&response);
if (err == OK && response != NULL) {
CHECK(response->findInt32("err", &err));
}
return err;
}
==>
status_t AMessage::postAndAwaitResponse(sp<AMessage> *response) {
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);
}
对于非阻塞的消息来说,也就是异步处理的消息,当消息创建好了就会调用该消息的post函数将该消息发用出去。而阻塞消息,也就是需要同步处理的消息来说,不调用post函数而是调用postAndAwaitResponse。在postAndAwaitResponse函数里间接会调用该消息对象的post函数。
在postAndAwaitResponse函数里将创建的AReplyToken对象的引用设置到要发送的消息里。
sp< AReplyToken> token = looper->createReplyToken();创建一个AReplyToken对象,这个AReplyToken对象是实现同步消息的桥梁。setObject(“replyID”, token);将该AReplyToken对象的引用添加到该消息中。
然后调用return looper->awaitResponse(token, response);
在looper->awaitResponse函数里,是作为等待应答消息完成设置的一方,循环等待,直到获取到应答消息为止。
3.当前进程循环等待应答消息
==>
return looper->awaitResponse(token, response);
==>
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;
}
循环等待应答消息直到成功获取到应答消息的引用,应答消息的引用由sp< AMessage> *response传回。
4.阻塞消息处理
下面贴出应对上一个例子的处理:
case kWhatGetSelectedTrack:
{
status_t err = INVALID_OPERATION;
if (mSource != NULL) {
err = OK;
int32_t type32;
CHECK(msg->findInt32("type", (int32_t*)&type32));
media_track_type type = (media_track_type)type32;
ssize_t selectedTrack = mSource->getSelectedTrack(type);
Parcel* reply;
CHECK(msg->findPointer("reply", (void**)&reply));
reply->writeInt32(selectedTrack);
}
sp<AMessage> response = new AMessage;
response->setInt32("err", err);
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
response->postReply(replyID);
break;
}
首先准备好我们要回应的消息:
sp<AMessage> response = new AMessage;
response->setInt32("err", err);
然后从消息中获取到阻塞消息处理的桥梁AReplyToken对象,是通过消息对象msg的senderAwaitsResponse(&replyID)函数来完成的:
==>
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
==>
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;
}
==>
bool found = findObject("replyID", &tmp);
==>
setObject("replyID", tmp);
获取到AReplyToken对象的引用后,调用准好的应答消息对象提供的外部函数postReply来完成应答消息的设置。
源码如下:
==>
response->postReply(replyID);
==>
status_t AMessage::postReply(const sp<AReplyToken> &replyToken) {
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);
}
==>
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;
}
==>
status_t AReplyToken::setReply(const sp<AMessage> &reply) {
if (mReplied) {
ALOGE("trying to post a duplicate reply");
return -EBUSY;
}
CHECK(mReply == NULL);
mReply = reply;
mReplied = true;
return OK;
}
通过调用AReplyToken对象的getLooper()函数得到的是AReplyToken对象含有的ALooper对象的引用,这个引用是在阻塞消息调用postAndAwaitResponse时,该阻塞消息含有的ALooper对象的引用调用createReplyToken传递给AReplyToken构造函数的,也就是说,阻塞消息对象和对应的AReplyToken对象同时都拥有ALooper对象的引用。
小结:
循环等待应答消息一方:
1.创建好阻塞消息msg。
2.调用阻塞消息msg的postAndAwaitResponse函数。
3.在msg的postAndAwaitResponse函数里调用该消息msg的成员变量mLooper的createReplyToken函数创建AReplyToken对象。
4.将创建好的AReplyToken对象的引用添加到msg消息里。
5.调用该消息msg的成员变量mLooper的post函数将携带有AReplyToken对象引用的消息发送出去。
6.调用消息msg的成员变量mLooper的awaitResponse函数循环等待应答消息成功被设置。
处理阻塞消息msg的一方:
1.创建好应答消息response。
2.调用消息msg的enderAwaitsResponseALooper函数用于得到阻塞消息含有的AReplyToken对象的引用。
3.调用应答消息response的postReply(const sp< AReplyToken> &replyToken, const sp< AMessage> &reply)函数完成后续的应答消息的设置。
其实总得来说阻塞消息设计并不复杂,设计的原理是生产者-消费者关系。消费者循环等待直到生产者生产出了自己需要的产品。中间的BUFFER,在这里是AReplyToken对象。对于安卓N版本阻塞消息处理机制设计繁琐,不够清晰,希望在后续的版本能够完善设计。让阻塞消息机制的设计看起来更加自然,使用起来也更加便捷。