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版本阻塞消息处理机制设计繁琐,不够清晰,希望在后续的版本能够完善设计。让阻塞消息机制的设计看起来更加自然,使用起来也更加便捷。
  

0 0