AMessage简析

作为AMessage/ALooper/AHandler中最复杂的一环, AMessage被我放到了最后来讲. 希望能讲的简洁明了.

声明

#ifndef A_MESSAGE_H_

#define A_MESSAGE_H_

#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/ALooper.h>
#include <utils/KeyedVector.h>
#include <utils/RefBase.h>

namespace android {

struct ABuffer;
struct AHandler;
struct AString;
class Parcel;
//在讲ALooper的时候已经讲过了
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;
    //获得指定Looper的使用权
    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);
};

struct AMessage : public RefBase {
    AMessage();
    AMessage(uint32_t what, const sp<const AHandler> &handler);

    // Construct an AMessage from a parcel.
    // nestingAllowed determines how many levels AMessage can be nested inside
    // AMessage. The default value here is arbitrarily set to 255.
    // FromParcel() returns NULL on error, which occurs when the input parcel
    // contains
    // - an AMessage nested deeper than maxNestingLevel; or
    // - an item whose type is not recognized by this function.
    // Types currently recognized by this function are:
    //   Item types      set/find function suffixes
    //   ==========================================
    //     int32_t                Int32
    //     int64_t                Int64
    //     size_t                 Size
    //     float                  Float
    //     double                 Double
    //     AString                String
    //     AMessage               Message

    //从Parcel中获取AMessage
    static sp<AMessage> FromParcel(const Parcel &parcel,
                                   size_t maxNestingLevel = 255);

    // Write this AMessage to a parcel.
    // All items in the AMessage must have types that are recognized by
    // FromParcel(); otherwise, TRESPASS error will occur.
    //将Parcel写入AMessage
    void writeToParcel(Parcel *parcel) const;
    //设置whay值相关(用于传递)
    void setWhat(uint32_t what);
    uint32_t what() const;
    
    void setTarget(const sp<const AHandler> &handler);

    void clear();
    //以下就是各种set值了(用于传递消息)
    void setInt32(const char *name, int32_t value);
    void setInt64(const char *name, int64_t value);
    void setSize(const char *name, size_t value);
    void setFloat(const char *name, float value);
    void setDouble(const char *name, double value);
    void setPointer(const char *name, void *value);
    void setString(const char *name, const char *s, ssize_t len = -1);
    void setString(const char *name, const AString &s);
    void setObject(const char *name, const sp<RefBase> &obj);
    void setBuffer(const char *name, const sp<ABuffer> &buffer);
    void setMessage(const char *name, const sp<AMessage> &obj);

    void setRect(
            const char *name,
            int32_t left, int32_t top, int32_t right, int32_t bottom);

    bool contains(const char *name) const;
    //以下是对应的各种find值(用于得到传递的消息)
    bool findInt32(const char *name, int32_t *value) const;
    bool findInt64(const char *name, int64_t *value) const;
    bool findSize(const char *name, size_t *value) const;
    bool findFloat(const char *name, float *value) const;
    bool findDouble(const char *name, double *value) const;
    bool findPointer(const char *name, void **value) const;
    bool findString(const char *name, AString *value) const;
    bool findObject(const char *name, sp<RefBase> *obj) const;
    bool findBuffer(const char *name, sp<ABuffer> *buffer) const;
    bool findMessage(const char *name, sp<AMessage> *obj) const;

    // finds any numeric type cast to a float
    bool findAsFloat(const char *name, float *value) const;

    bool findRect(
            const char *name,
            int32_t *left, int32_t *top, int32_t *right, int32_t *bottom) const;
    //post消息
    status_t post(int64_t delayUs = 0);

    // Posts the message to its target and waits for a response (or error)
    // before returning.
    //同步操作, post消息等待回复
    status_t postAndAwaitResponse(sp<AMessage> *response);

    // If this returns true, the sender of this message is synchronously
    // awaiting a response and the reply token is consumed from the message
    // and stored into replyID. The reply token must be used to send the response
    // using "postReply" below.
    bool senderAwaitsResponse(sp<AReplyToken> *replyID);

    // Posts the message as a response to a reply token.  A reply token can
    // only be used once. Returns OK if the response could be posted; otherwise,
    // an error.
    status_t postReply(const sp<AReplyToken> &replyID);

    // Performs a deep-copy of "this", contained messages are in turn "dup'ed".
    // Warning: RefBase items, i.e. "objects" are _not_ copied but only have
    // their refcount incremented.
    sp<AMessage> dup() const;

    // Performs a shallow or deep comparison of |this| and |other| and returns
    // an AMessage with the differences.
    // Warning: RefBase items, i.e. "objects" are _not_ copied but only have
    // their refcount incremented.
    // This is true for AMessages that have no corresponding AMessage equivalent in |other|.
    // (E.g. there is no such key or the type is different.) On the other hand, changes in
    // the AMessage (or AMessages if deep is |false|) are returned in new objects.
    sp<AMessage> changesFrom(const sp<const AMessage> &other, bool deep = false) const;

    AString debugString(int32_t indent = 0) const;
    //键值对中的"键"
    enum Type {
        kTypeInt32,
        kTypeInt64,
        kTypeSize,
        kTypeFloat,
        kTypeDouble,
        kTypePointer,
        kTypeString,
        kTypeObject,
        kTypeMessage,
        kTypeRect,
        kTypeBuffer,
    };

    size_t countEntries() const;
    const char *getEntryNameAt(size_t index, Type *type) const;

protected:
    virtual ~AMessage();

private:
    friend struct ALooper; // deliver()

    uint32_t mWhat;

    // used only for debugging
    ALooper::handler_id mTarget;

    wp<AHandler> mHandler;
    wp<ALooper> mLooper;

    struct Rect {
        int32_t mLeft, mTop, mRight, mBottom;
    };
    //键值对中的"值"
    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);
    };

    enum {
        kMaxNumItems = 64
    };
    Item mItems[kMaxNumItems];
    size_t mNumItems;

    Item *allocateItem(const char *name);
    void freeItemValue(Item *item);
    const Item *findItem(const char *name, Type type) const;

    void setObjectInternal(
            const char *name, const sp<RefBase> &obj, Type type);

    size_t findItemIndex(const char *name, size_t len) const;

    void deliver();

    DISALLOW_EVIL_CONSTRUCTORS(AMessage);
};

}  // namespace android

#endif  // A_MESSAGE_H_

在讲头文件进行了简要的注释后, 我们来看看CPP文件中, 几个以后比较常见也比较重要的方法是长什么样子的.

构造函数

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);
}

可以看到, 有两个构造函数.

  1. 不带参数的构造函数三个字段直接全部赋值为0
  2. 带参数的构造函数, 将发送的what值和发送哪一个handler(处理者)指定了
    有同学会问, setTarget具体长什么样子? 好嘛, 给你看.
void AMessage::setTarget(const sp<const AHandler> &handler) {
    if (handler == NULL) {
        mTarget = 0;
        mHandler.clear();
        mLooper.clear();
    } else {
        mTarget = handler->id();
        mHandler = handler->getHandler();
        mLooper = handler->getLooper();
    }
}

其实, 就是把handler的ID呀, handler自身呀和handler所在的Looper拿到了手中.

set/find系列相关方法

在了解各种setXXX和findXXX方法之前, 有两个关于Item的方法, 值得我们看一下.

1. void AMessage::Item::setName(const char *name, size_t len)

// assumes item's name was uninitialized or NULL
void AMessage::Item::setName(const char *name, size_t len) {
    mNameLength = len;
    mName = new char[len + 1];
    memcpy((void*)mName, name, len + 1);
}

这个函数做了两件事情

  1. 将设置的len给了mNameLength字段, 标明name的长度;
  2. 分配空间, 将设置的name复制到分配给字段mName的内存空间中;

2. inline size_t AMessage::findItemIndex(const char *name, size_t len) const

inline size_t AMessage::findItemIndex(const char *name, size_t len) const {
#ifdef DUMP_STATS
    size_t memchecks = 0;
#endif
    size_t i = 0;
    for (; i < mNumItems; i++) {
        if (len != mItems[i].mNameLength) {
            continue;
        }
#ifdef DUMP_STATS
        ++memchecks;
#endif
        if (!memcmp(mItems[i].mName, name, len)) {
            break;
        }
    }
#ifdef DUMP_STATS
    {
        Mutex::Autolock _l(gLock);
        ++gFindItemCalls;
        gAverageNumItems += mNumItems;
        gAverageNumMemChecks += memchecks;
        gAverageNumChecks += i;
        reportStats();
    }
#endif
    return i;
}

这里分析下没有打开宏DUMP_STATS的情况.

  1. 首先看到一个for循环, 其实就是在这个循环中, 找到我们设置的len长度相匹配的名字(长度为len, 名字为name的项)
  2. 返回这个项的下标

3. AMessage::Item *AMessage::allocateItem(const char *name)

AMessage::Item *AMessage::allocateItem(const char *name) {
    size_t len = strlen(name);
    size_t i = findItemIndex(name, len);
    Item *item;

    if (i < mNumItems) {
        item = &mItems[i];
        freeItemValue(item);
    } else {
        CHECK(mNumItems < kMaxNumItems);
        i = mNumItems++;
        item = &mItems[i];
        item->setName(name, len);
    }

    return item;
}

这个方法里面包含了一个freeItemValue, 什么滴干活? 释放资源的干活, 不单独讲了.

void AMessage::freeItemValue(Item *item) {
    switch (item->mType) {
        case kTypeString:
        {
            delete item->u.stringValue;
            break;
        }

        case kTypeObject:
        case kTypeMessage:
        case kTypeBuffer:
        {
            if (item->u.refValue != NULL) {
                item->u.refValue->decStrong(this);
            }
            break;
        }

        default:
            break;
    }
}

很简单, 说明一下

  1. 获取到要设置的name的len, 并根据这个len和name在Items中找找看有没有设置过;
  2. 如果有, 那就删除该位置的值(stringValue或者是refValue的情况); 否则, 给mNumItems数组的下一个空的元素赋值
  3. 返回这个item(注意: 是一个指针, 指向刚刚操作过的元素)

4. const AMessage::Item * AMessage::findItem(const char *name, Type type)

const AMessage::Item *AMessage::findItem(
        const char *name, Type type) const {
    size_t i = findItemIndex(name, strlen(name));
    if (i < mNumItems) {
        const Item *item = &mItems[i];
        return item->mType == type ? item : NULL;

    }
    return NULL;
}
  1. 首先在Items中寻找看有木有对应的上name和type的Item
  2. 如果有, 再看type是否对的上; 对的上的返回这个item, 其他情况, 一概返回NULL

5. void AMessage::setObjectInternal(const char *name, const sp<RefBase> &obj, Type type)

void AMessage::setObjectInternal(
        const char *name, const sp<RefBase> &obj, Type type) {
    Item *item = allocateItem(name);
    item->mType = type;

    if (obj != NULL) { obj->incStrong(this); }
    item->u.refValue = obj.get();
}

这个方法也是很基础的, 简单来说

  1. 为一个Item对象分配了内存, 并且对type字段赋值
  2. 如果传入的指针obj不为空, 变为强指针且, 获得这个指针(赋值给refValue)

6. 两个重要的setXXX

1) AMessage::setBuffer

void AMessage::setBuffer(const char *name, const sp<ABuffer> &buffer) {
    setObjectInternal(name, sp<RefBase>(buffer), kTypeBuffer);
}

调用了setObjectInternal, 将name和指向这段buffer的指针对应起来了(通过创建的Item(键值对))

2) AMessage::setMessage

void AMessage::setMessage(const char *name, const sp<AMessage> &obj) {
    Item *item = allocateItem(name);
    item->mType = kTypeMessage;

    if (obj != NULL) { obj->incStrong(this); }
    item->u.refValue = obj.get();
}

同样, 调用了setObjectInternal, 将name和指向这个AMessage的指针对应起来了(通过创建的Item(键值对))

7. 对应的findXXX

findXXX系列方法大同小异, 这里仅仅选择一个作为代表吧

bool AMessage::findBuffer(const char *name, sp<ABuffer> *buf) const {
    const Item *item = findItem(name, kTypeBuffer);
    if (item) {
        *buf = (ABuffer *)(item->u.refValue);
        return true;
    }
    return false;
}
  1. 在键值对Items中, 根据name找到item;
  2. 如果找到了, 将ABuff类型的指针, 指向之前setBuffer设置好的buffer

8. void AMessage::deliver()

347    void AMessage::deliver() {
348    sp<AHandler> handler = mHandler.promote();
349    if (handler == NULL) {
350        ALOGW("failed to deliver message as target handler %d is gone.", mTarget);
351        return;
352    }
353
354    handler->deliverMessage(this);
355}
  1. 首先绑定上一个处理者handler;
  2. 使用handler的deliverMessage方法;

deliverMessage方法, 我们在<AHanlder简析>一节中看到过, 就是放到Message的onMessageReceived中处理, 有兴趣的童鞋可以返回去看看, 这里不再说明了.

9. 发送消息相关方法

9.1 status_t AMessage::post(int64_t delayUs)

357    status_t AMessage::post(int64_t delayUs) {
358    sp<ALooper> looper = mLooper.promote();
359    if (looper == NULL) {
360        ALOGW("failed to post message as target looper for handler %d is gone.", mTarget);
361        return -ENOENT;
362    }
363
364    looper->post(this, delayUs);
365    return OK;
366}
  1. 首先绑定了在构造函数中就已传入的消息监测者ALooper;
  2. 调用了looper的post. 这个post的方法在<ALooper>一节中讲到过, 这里就不再敖述.

9.2 AMessage::postAndAwaitResponse(sp<AMessage> *response)

368    status_t AMessage::postAndAwaitResponse(sp<AMessage> *response) {
369    sp<ALooper> looper = mLooper.promote();
370    if (looper == NULL) {
371        ALOGW("failed to post message as target looper for handler %d is gone.", mTarget);
372        return -ENOENT;
373    }
374
375    sp<AReplyToken> token = looper->createReplyToken();
376    if (token == NULL) {
377        ALOGE("failed to create reply token");
378        return -ENOMEM;
379    }
380    setObject("replyID", token);
381
382    looper->post(this, 0 /* delayUs */);
383    return looper->awaitResponse(token, response);
384    }
  1. 绑定上构造函数中就传入了ALooper;
  2. 创建一个AReplyToken(在ALooper中讲过);
  3. 将name"replyID"和这个token绑定在一起形成键值对;
  4. 立即post这个消息;
  5. 返回post这个消息后, 接收方返回的消息;

9.3 status_t AMessage::postReply(const sp<AReplyToken> &replyToken)

386    status_t AMessage::postReply(const sp<AReplyToken> &replyToken) {
387    if (replyToken == NULL) {
388        ALOGW("failed to post reply to a NULL token");
389        return -ENOENT;
390    }
391    sp<ALooper> looper = replyToken->getLooper();
392    if (looper == NULL) {
393        ALOGW("failed to post reply as target looper is gone.");
394        return -ENOENT;
395    }
396    return looper->postReply(replyToken, this);
397    }

和9.2所讲到的postAndAwaitResponse对应. 这里就是把AReplayToken和消息回复给传递消息过来, 并且要求回复的

9.4

399    bool AMessage::senderAwaitsResponse(sp<AReplyToken> *replyToken) {
400    sp<RefBase> tmp;
401    bool found = findObject("replyID", &tmp);
402
403    if (!found) {
404        return false;
405    }
406
407    *replyToken = static_cast<AReplyToken *>(tmp.get());
408    tmp.clear();
409    setObject("replyID", tmp);
410    // TODO: delete Object instead of setting it to NULL
411
412    return *replyToken != NULL;
413    }

经过前面两个方法的洗礼, 这里大概大家都能一眼看书来, 这里就是换一个replayToken吧

总结

分析到这里, 算是告一段落了吧. 因为时间和水平有限, 讲解的难免会有纰漏和错误, 还请各位看官批评指正.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容