Android native层媒体通信架构AHandler/ALooper机制实现源码分析【Part 2】

承接上一章节分析:【若前一章节没看过,建议先看上一章节】
Android native层媒体通信架构AHandler/ALooper机制实现源码分析【Part 1】
本系列文章分析的安卓源码版本:【Android 10.0 版本】

3、发送消息:
ALooper内部线程或其他线程中均可发送消息。
【以MediaClock为例】

// [frameworks/av/media/libstagefright/MediaClock.cpp]
void MediaClock::processTimers_l() {
	// 省略其他代码
	
	// 发送该事件消息,可延迟发送并携带参数
	
	// 见3.1小节分析
    sp<AMessage> msg = new AMessage(kWhatTimeIsUp, this);
    // 传递参数,可传递非常多的数据类型
    // 见3.2小节分析
    msg->setInt32("generation", mGeneration);
    // post发送事件消息
    // 见3.3小节分析
    msg->post(nextLapseRealUs);
}

3.1、AMessage类声明:
省略其他代码

// [frameworks/av/media/libstagefright/foundation/include/media/stagefright/foundation/AMessage.h]
struct AMessage : public RefBase {}

AMessage构造函数实现:
有两个构造函数:无参构造函数和有参构造函数

// [frameworks/av/media/libstagefright/foundation/AMessage.cpp]
AMessage::AMessage(void)
	// 默认全为0
    : mWhat(0),
      mTarget(0),
      mNumItems(0) {
}

// [frameworks/av/media/libstagefright/foundation/AMessage.cpp]
AMessage::AMessage(uint32_t what, const sp<const AHandler> &handler)
	// 事件常量值即事件id
    : mWhat(what),
    // 事件个数,默认为0
      mNumItems(0) {
      // 设置目标handler对象
      // 见下面的分析
    setTarget(handler);
}

setTarget(handler)实现分析:

// [frameworks/av/media/libstagefright/foundation/AMessage.cpp]
void AMessage::setTarget(const sp<const AHandler> &handler) {
	// 判断handler消息接收处理器是否存在
    if (handler == NULL) {
    	// handler 为空时,直接清除工作,
    	// 清空当前AMessage对象中可能缓存的AHandler和ALooper对象,并将mTarget置为0 。
    	// 注:mTarget该值为handler id
        mTarget = 0;
        mHandler.clear();
        mLooper.clear();
    } else {
    	// handler不为空时,则获取这些值
        mTarget = handler->id();
        mHandler = handler->getHandler();
        mLooper = handler->getLooper();
    }
}

3.2、AMessage 传递参数,可传递非常多的数据类型,可调用 setXXX() 方法来传递。此处举例分析几个典型的数据处理过程。

// 传递一个Key值名称为“generation”的参数数据mGeneration
msg->setInt32("generation", mGeneration);

主要实现的设置数据参数setXXX()方法有如下几个声明:【省略其他代码】
第一个参数都是作为key值字符串(字段)来标识存储对应的字段值。

// [frameworks/av/media/libstagefright/foundation/include/media/stagefright/foundation/AMessage.h]
struct AMessage : public RefBase {
    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);
}

支持非常多数据类型的参数。
但当我们查看其实现文件中的定义实现时,发现这些方法前面几个方法没有对应的定义实现。其实我们仔细找就可以发现,有部分方法定义实现其实是使用的宏定义来完成。
如下基础数据类型宏定义实现:

// [frameworks/av/media/libstagefright/foundation/AMessage.cpp]

// 由该宏定义可知,有三个参数:方法名字组成部分(NAME)、字段名(FIELDNAME)、数据类型名(TYPENAME)
#define BASIC_TYPE(NAME,FIELDNAME,TYPENAME)                             \
void AMessage::set##NAME(const char *name, TYPENAME value) {            \
	// 分配Key值名称为(name)参数名的(新或旧)可用数据项对象指针
	// 见3.2.1小节分析
    Item *item = allocateItem(name);                                    \
                                                                        \
    // 数据项数据类型赋值
    item->mType = kType##NAME;
    // 数据项参数数据赋值                                          \
    item->u.FIELDNAME = value;                                          \
}                                                                       \
                                                                        \
// NOLINT含义:简单了解就是避免clang编译器编译时报错(警告报错等)
/* NOLINT added to avoid incorrect warning/fix from clang.tidy */       \
bool AMessage::find##NAME(const char *name, TYPENAME *value) const {  /* NOLINT */ \
	// (在数据项 Item 类型数组参数集中)
	// 查找指定数据项Key值名称(name)的数据项对象,返回其一个对象指针
	// 见3.2.2小节分析
    const Item *item = findItem(name, kType##NAME);                     \
    if (item) {                                                         \
    	// 查询到该数据项时,则赋值给value指针来存储该参数值,并返回true即查找成功
        *value = item->u.FIELDNAME;                                     \
        return true;                                                    \
    }                                                                   \
    // 未查找到该参数值则返回false
    return false;                                                       \
}

// 6种 setXXX() 设置数据参数方法通过宏定义来完成实现
BASIC_TYPE(Int32,int32Value,int32_t)
BASIC_TYPE(Int64,int64Value,int64_t)
BASIC_TYPE(Size,sizeValue,size_t)
BASIC_TYPE(Float,floatValue,float)
BASIC_TYPE(Double,doubleValue,double)
BASIC_TYPE(Pointer,ptrValue,void *)

#undef BASIC_TYPE

因此由上面宏定义实现可知,有6种 setXXX() 设置数据参数方法通过宏定义来完成实现,那么也肯定会有对应的6种 findXXX() 查询获取对应数据参数值的方法。
并且存储的每一个参数数据都是通过一个创建一个Key值名称为(name)参数名的数据项对象来缓存的,并且最终会将整个AMessage中的每个参数数据都添加到数据项 Item 类型的数组参数集中。【注意:该数组只允许最多携带64个数据项数据,若超出则程序将抛错并停止运行】
由该部分小节分析可知,AMessage添加每个参数时,若此前已添加过该Key值参数,那么将会覆盖前面的值。
警告:根据上面完整分析可知,就算是数据项数据类型不同,也要求每个数据项的Key值必须不能相同,否则会被重置参数值。
其提供主要实现的findXXX()方法声明如下:【省略其他代码】
第一个参数都是作为key值字符串(字段)来查找对应的数据参数值。

// [frameworks/av/media/libstagefright/foundation/include/media/stagefright/foundation/AMessage.h]
struct AMessage : public RefBase {
    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 signed integer types cast to int64_t
    bool findAsInt64(const char *name, int64_t *value) 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;
}

【Item】数据项的相关定义如下,是AMessage的内部struct类结构:【省略其他代码】

// [frameworks/av/media/libstagefright/foundation/include/media/stagefright/foundation/AMessage.h]
struct AMessage : public RefBase {
    enum Type {
        kTypeInt32,
        kTypeInt64,
        kTypeSize,
        kTypeFloat,
        kTypeDouble,
        kTypePointer,
        kTypeString,
        kTypeObject,
        kTypeMessage,
        kTypeRect,
        kTypeBuffer,
    };
    
    struct Rect {
        int32_t mLeft, mTop, mRight, mBottom;
    };

private:
    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;
        // 当前数据项Key值名称字符串指针
        const char *mName;
        // 缓存当前数据项Key值名称字符串长度
        size_t      mNameLength;
        // 当前数据项数据类型
        Type mType;
        // 设置数据项Key值名称
        // 见下面的分析
        void setName(const char *name, size_t len);
    };
}

item->setName(name, len)实现分析:
设置数据项Key值名称

// [frameworks/av/media/libstagefright/foundation/AMessage.cpp]
// assumes item's name was uninitialized or NULL
void AMessage::Item::setName(const char *name, size_t len) {
    mNameLength = len;
    // 注意此处的字符串指针内存申请的处理,必现用字符串名称长度len加1,
    // 因为len长度不包含空值字符,
    // 因此字符串必现包含空值字符串即'\0',否在其只是个字符数组而不是字符串
    mName = new char[len + 1];
    // 复制字符串到新分配内存中
    memcpy((void*)mName, name, len + 1);
}

3.2.1、allocateItem(name)实现分析:
分配Key值名称为(name)参数名的数据项对象指针,即返回一个可用数据项对象指针【新数据项或已释放参数数据的旧数据项】
警告:根据下面的分析可知,就算是数据项数据类型不同,也要求每个数据项的Key值必须不能相同,否则会被重置参数值。

// [frameworks/av/media/libstagefright/foundation/AMessage.cpp]
AMessage::Item *AMessage::allocateItem(const char *name) {
	// 获取参数Key值名称字符串长度
    size_t len = strlen(name);
    // (在数据项 Item 类型数组参数集中)查找指定数据项Key值名称的数据项索引
    // 注意这个返回值的含义:
	// 返回查找到的索引值,若不存在则默认返回当前需要创建的数据索引
	// 即此时i值和mNumItems数组大小值相同(若无数据时i=mNumItems=0)
    // findItemIndex() 见3.2.1.1小节分析
    size_t i = findItemIndex(name, len);
    Item *item;

    // 备注:AMessage构造函数初始化时 mNumItems 该值为0
    if (i < mNumItems) {
    	// 查找到旧数据项时
        item = &mItems[i];
        // 有旧数据项时,则释放该数据项所持有的数据参数值
        // freeItemValue() 见3.2.1.2小节分析
        freeItemValue(item);
    } else {
    	// 未查找到旧数据项时
    	
    	// 检查已添加的参数数据项总个数
    	// 【kMaxNumItems常量为64,若超出则程序将抛错并停止运行】
        CHECK(mNumItems < kMaxNumItems);
        // i先使用mNumItems的值,然后mNumItems值再加1
        i = mNumItems++;
        // 获取i索引对应的新数据项对象的地址,即赋值给指针
        item = &mItems[i];
        // 默认数据项数据类型为 kTypeInt32
        item->mType = kTypeInt32;
        // 设置数据项Key值名称
        item->setName(name, len);
    }

    // 然后返回一个可用数据项对象指针【新数据项或已释放参数数据的旧数据项】
    return item;
}

3.2.1.1、findItemIndex(name, len)实现分析:
(在数据项 Item 类型数组参数集中)查找指定数据项Key值名称的数据项索引。

// [frameworks/av/media/libstagefright/foundation/AMessage.cpp]
inline size_t AMessage::findItemIndex(const char *name, size_t len) const {
// DUMP_STATS 宏定义在AMessage.cpp最前面默认是未定义。【被注释掉了】
// 其主要作用就是收集数据统计
#ifdef DUMP_STATS
    size_t memchecks = 0;
#endif
	// for循环处理:循环匹配指定数据项Key值名称的数据项及其查询其存在的索引
	// 备注:AMessage构造函数初始化时 mNumItems 该值为0
    size_t i = 0;
    for (; i < mNumItems; i++) {
    	// 首先通过名称长度来匹配,可以更加高效的完成匹配
        if (len != mItems[i].mNameLength) {
            continue;
        }
#ifdef DUMP_STATS
		// 该值记录相同长度的数据项Key值名称所执行的匹配次数
        ++memchecks;
#endif
        // 使用memcmp()方法来匹配,返回0则表示匹配成功,否则匹配失败继续下一次循环匹配
        if (!memcmp(mItems[i].mName, name, len)) {
            break;
        }
    }
#ifdef DUMP_STATS
	// 此处就不分析了,默认不开启。主要就是统计数据【统计本次查询的效率】
    {
        Mutex::Autolock _l(gLock);
        ++gFindItemCalls;
        gAverageNumItems += mNumItems;
        gAverageNumMemChecks += memchecks;
        gAverageNumChecks += i;
        // 该方法就是上报统计数据,其实际就是LOGI打印这些数据,然后reset它们
        reportStats();
    }
#endif
	// 注意这个返回值的含义:
	// 返回查找到的索引值,若不存在则默认返回当前需要创建的数据索引
	// 即此时i值和mNumItems数组大小值相同(若无数据时i=mNumItems=0)
    return i;
}

3.2.1.2、freeItemValue(item)实现分析:
释放该数据项所持有的数据参数值

// [frameworks/av/media/libstagefright/foundation/AMessage.cpp]
void AMessage::freeItemValue(Item *item) {
	// 判断数据项数据类型
    switch (item->mType) {
        case kTypeString:
        {
            // 若是String字符串类型,则使用delete释放其携带的数据参数值
            delete item->u.stringValue;
            break;
        }

        case kTypeObject:
        case kTypeMessage:
        case kTypeBuffer:
        {
            // 若为Object、Message、Buffer类型,则若其数据参数对象指针不为空时,
            // 减少其携带的数据参数对象指针强引用计数(可能会释放若计数为0的话)
            if (item->u.refValue != NULL) {
                item->u.refValue->decStrong(this);
            }
            break;
        }

        default:
            break;
    }
    // 清除数据项数据类型,并初始化为 kTypeInt32
    item->mType = kTypeInt32; // clear type
}

3.2.2、findItem(name, kType##NAME)实现分析:
(在数据项 Item 类型数组参数集中)查找指定数据项Key值名称(name)的数据项对象,返回其一个对象指针
备注:通过下面的分析可知,查找指定Key值数据时,就算是Key值相同,也会再次检查其数据类型是否相同,若不相同则返回NULL。

// [frameworks/av/media/libstagefright/foundation/AMessage.cpp]
const AMessage::Item *AMessage::findItem(
        const char *name, Type type) const {
    // (在数据项 Item 类型数组参数集中)查找指定数据项Key值名称的数据项索引
    // 注意这个返回值的含义:
	// 返回查找到的索引值,若不存在则默认返回当前需要创建的数据索引
	// 即此时i值和mNumItems数组大小值相同(若无数据时i=mNumItems=0)
    // findItemIndex() 见前面的3.2.1.1小节分析
    size_t i = findItemIndex(name, strlen(name));
    // 备注:AMessage构造函数初始化时 mNumItems 该值为0
    if (i < mNumItems) {
    	// 查询到对应的数据项,赋值给对象指针
        const Item *item = &mItems[i];
        // 然后再判断数据项数据类型释放一致,一致则返回,否在返回NULL
        return item->mType == type ? item : NULL;

    }
    // 未查找到则返回NULL
    return NULL;
}

3.2.3、此处再分析一下另外几种数据类型的setXXX() 方法实现,如下

// [frameworks/av/media/libstagefright/foundation/include/media/stagefright/foundation/AMessage.h]
struct AMessage : public RefBase {
    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);
}

它们对应的实现:
均在该文件中 [frameworks/av/media/libstagefright/foundation/AMessage.cpp] 实现。实现都比较简单,只分析重点。

void AMessage::setString(
        const char *name, const char *s, ssize_t len) {
    // 分配并返回一个(新或旧)数据项对象指针
    Item *item = allocateItem(name);
    
    // 数据类型
    item->mType = kTypeString;
    // 重新复制一份当前字符串到新内存中
    item->u.stringValue = new AString(s, len < 0 ? strlen(s) : len);
}

void AMessage::setString(
        const char *name, const AString &s) {
    // 转换AString类型字符串为 const char *
    setString(name, s.c_str(), s.size());
}

// 该方法是设置【sp<RefBase>】对象类型数据项处理,此处传入的是强引用指针对象引用
void AMessage::setObjectInternal(
        const char *name, const sp<RefBase> &obj, Type type) {
    Item *item = allocateItem(name);
    item->mType = type;

    // 增加强引用计数
    // 注意:智能指针对象的【!=】不等运算符的含义,并不是判断obj是否为空,
    // 而是判断它内部持有的目标指针【RefBase *】是否为空,若不为空才增加强引用计数。
    if (obj != NULL) { obj->incStrong(this); }
    // 获取obj内部持有的目标指针【RefBase *】
    item->u.refValue = obj.get();
}

void AMessage::setObject(const char *name, const sp<RefBase> &obj) {
    setObjectInternal(name, obj, kTypeObject);
}

void AMessage::setBuffer(const char *name, const sp<ABuffer> &buffer) {
	// 直接将 sp<ABuffer> 类型数据转换为 sp<RefBase> 类型
    setObjectInternal(name, sp<RefBase>(buffer), kTypeBuffer);
}

// 存储参数为另一个AMessage数据类型的数据项,其实这个实现和上面两个方法实现类似的,
// 因此我们可以直接改写成:setObjectInternal(name, sp<RefBase>(obj), kTypeMessage);
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();
}

void AMessage::setRect(
        const char *name,
        int32_t left, int32_t top, int32_t right, int32_t bottom) {
    Item *item = allocateItem(name);
    item->mType = kTypeRect;

    item->u.rectValue.mLeft = left;
    item->u.rectValue.mTop = top;
    item->u.rectValue.mRight = right;
    item->u.rectValue.mBottom = bottom;
}

3.3、AMessage post发送事件消息实现分析
由于本章节篇幅过长,因此将后续分析部分放在本系列下一章节分析,请查看:
Android native层媒体通信架构AHandler/ALooper机制实现源码分析【Part 3】

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值