AReplyToken

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值