AReplyToken
来源:互联网 发布:营业执照制作软件下载 编辑:程序博客网 时间:2024/05/22 05:12
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版本阻塞消息处理机制设计繁琐,不够清晰,希望在后续的版本能够完善设计。让阻塞消息机制的设计看起来更加自然,使用起来也更加便捷。
- AReplyToken
- 【强连通分量】 hdu1269 迷宫城堡
- 5-3 逆序的三位数 (10分)
- 网易视频云海外加速方案
- Oracle decode使用
- A1021. Deepest Root (25)
- AReplyToken
- Spring MVC测试框架详解——服务端测试
- 【Java】public、private和protected的区别
- Core java note part02
- JAVA判断文件的编码格式
- 5-4 BCD解密 (10分)
- 安卓开发学习心得-------连续点击两次退出
- Java面向对象
- win10运行时突然内存被windows modules installer worker占去