彻底理解掌握native层媒体通信架构AHandler/ALooper消息循环机制实现源码分析【Part 1】
本系列文章分析的安卓源码版本:【Android 10.0 版本】
很久前分析过android java端Handler机制实现源码分析,并手写了该Handler机制的简单实现——【教你手写android底层Handler消息机制实现框架】
此章节的AHandler机制实现原理和java端大同小异,当然还有另外对应于java层Handler的native层对应实现,以后有时间可以考虑分析下,但实现原理都类似的。
推荐必备知识点:
native层线程实现原理请见我另一章节
Android native/C++层Thread线程实现源码分析
本章将分析native层媒体模块通信AHandler机制源码实现,类图关系如下:
AHandler消息机制功能基本使用:【我们直接参考MediaClock】
引入头文件:
#include <media/stagefright/foundation/AHandler.h>
#include <media/stagefright/foundation/AMessage.h>
只需要引入这两个头文件即可实现,而ALooper头文件不需要,因为ALooper头文件已在AHandler.h中引入:
// [frameworks/av/media/libstagefright/foudation/include/media/stagefright/foundation/AHandler.h]
#include <media/stagefright/foundation/ALooper.h>
MediaClock类声明,省略其他无关代码
// [frameworks/av/media/libstagefright/include/media/stagefright/MediaClock.h]
struct MediaClock : public AHandler {
virtual void onMessageReceived(const sp<AMessage> &msg);
private:
sp<ALooper> mLooper;
}
创建ALooper:
// [frameworks/av/media/libstagefright/MediaClock.cpp]
MediaClock::MediaClock()
: mAnchorTimeMediaUs(-1),
mAnchorTimeRealUs(-1),
mMaxTimeMediaUs(INT64_MAX),
mStartingTimeMediaUs(-1),
mPlaybackRate(1.0),
mGeneration(0) {
mLooper = new ALooper;
mLooper->setName("MediaClock");
mLooper->start(false /* runOnCallingThread */,
false /* canCallJava */,
ANDROID_PRIORITY_AUDIO);
}
设置AHandler:
// [frameworks/av/media/libstagefright/MediaClock.cpp]
void MediaClock::init() {
mLooper->registerHandler(this);
}
ALooper消息循环线程中的事件消息接收处理:
// [frameworks/av/media/libstagefright/MediaClock.cpp]
void MediaClock::onMessageReceived(const sp<AMessage> &msg) {
// 接收事件消息处理,并可获取消息中的参数数据
switch (msg->what()) {
case kWhatTimeIsUp:
{
int32_t generation;
CHECK(msg->findInt32("generation", &generation));
Mutex::Autolock autoLock(mLock);
if (generation != mGeneration) {
break;
}
processTimers_l();
break;
}
default:
TRESPASS();
break;
}
}
ALooper内部线程或其他线程中均可发送消息:
// [frameworks/av/media/libstagefright/MediaClock.cpp]
void MediaClock::processTimers_l() {
// 省略其他代码
// 发送该事件消息,可延迟发送并携带参数
sp<AMessage> msg = new AMessage(kWhatTimeIsUp, this);
msg->setInt32("generation", mGeneration);
msg->post(nextLapseRealUs);
}
结束ALooper消息循环线程:
// [frameworks/av/media/libstagefright/MediaClock.cpp]
MediaClock::~MediaClock() {
reset();
if (mLooper != NULL) {
mLooper->unregisterHandler(id());
mLooper->stop();
}
}
因此从上面的实现,可以看出native层Handler使用也非常简单的,
但native层Handler消息机制设计框架和java层Handler框架稍有不同,不同之处在于:
(1)native层Handler机制的Looper实现中会自动创建一个独立线程,而java层Handler需要应用层自己实现一个线程来完成,当然你也可以使用java层HandlerThread类更简单完成。
(2)一些类似相同作用的方法的属于实现者类有区别,即放置的地方不同如发送消息事件的方法,在java层位于Handler来完成发送消息并完成接收处理。而native层发送消息是由消息对象(AMessage)来完成发送,Handler来完成接收处理的。
因此可根据上面的基本使用流程来分析该AHandler/ALooper机制的源码实现。
1、ALooper类声明:
// [frameworks/av/media/libstagefright/foundation/include/media/stagefright/foundation/ALooper.h]
struct ALooper : public RefBase {
// 定义两个类型别名
typedef int32_t event_id;
typedef int32_t handler_id;
// ... 省略其他
}
ALooper构造函数:
初始化一个变量【mRunningLocally】,并执行清除任务
根据后续分析可知该变量作用为:
是否本地(线程)运行即在调用者线程中运行,若为true则将在调用者线程中运行循环消息方法,默认为false。
// [frameworks/av/media/libstagefright/foundation/ALooper.cpp]
ALooper::ALooper()
: mRunningLocally(false) {
// clean up stale AHandlers. Doing it here instead of in the destructor avoids
// the side effect of objects being deleted from the unregister function recursively.
// 清除已注册的但已无效不使用的AHandler,注意若还有效则不会清除
// 该实现见下面的分析
gLooperRoster.unregisterStaleHandlers();
}
gLooperRoster.unregisterStaleHandlers()实现分析:
gLooperRoster该对象定义,其为当前文件中全局变量【即ALooper和AHandler对应关联项的花名册】。
实际上该类对象的作用就是一个全局记录所有线程中使用到ALooperr和AHandler对应关联项的名单。
// [frameworks/av/media/libstagefright/foundation/ALooper.cpp]
ALooperRoster gLooperRoster;
ALooperRoster类声明和构造函数实现:
ALooperRoster类声明
// [frameworks/av/media/libstagefright/foundation/include/media/stagefright/foundation/ALooperRoster.h]
struct ALooperRoster {
// ... 省略其他代码
}
ALooperRoster构造函数实现
// [frameworks/av/media/libstagefright/foundation/ALooperRoster.cpp]
ALooperRoster::ALooperRoster()
// 下一个handler id,默认为1
: mNextHandlerID(1) {
}
unregisterStaleHandlers()该方法在ALooperRoster类中的声明和定义实现:
该方法功能为清除已注册的但已无效不使用的AHandler对象关联表信息,注意若还有效则不会清除。
【省略其他代码】
// [frameworks/av/media/libstagefright/foundation/include/media/stagefright/foundation/ALooperRoster.h]
struct ALooperRoster {
void unregisterStaleHandlers();
}
// [frameworks/av/media/libstagefright/foundation/ALooperRoster.cpp]
void ALooperRoster::unregisterStaleHandlers() {
Vector<sp<ALooper> > activeLoopers;
// 加锁代码块
{
Mutex::Autolock autoLock(mLock);
// mHandlers为ALooper和AHandler对象关联信息表
// 其声明为:KeyedVector<ALooper::handler_id, HandlerInfo> mHandlers;
// 即通过handler id来查找对应的关联表中一项关联信息。
// 该变量的赋值实现分析可见下面相关小节中的分析
for (size_t i = mHandlers.size(); i > 0;) {
// 关联信息列表从最后一个开始查询
i--;
// 由上面的类图可知,该类信息即为ALooper和AHandler对象一项关联信息,
// 并都使用弱引用(对象)关联缓存
const HandlerInfo &info = mHandlers.valueAt(i);
// 通过ALooper的弱引用指针对象提升为强引用指针对象
// 若转换成功则引用计数将加上1
sp<ALooper> looper = info.mLooper.promote();
if (looper == NULL) {
// 若为空则表明当前ALooper强引用对象指针已经被释放了,因此直接remove该关联项信息即可
ALOGV("Unregistering stale handler %d", mHandlers.keyAt(i));
mHandlers.removeItemsAt(i);
} else {
// 注意需要理解此处的处理效果:
// 即如英文注释所述,必须要使用加锁代码块外的vector对象集合
// 来临时缓存记录需要被减少(释放)强引用计数对象,
// 此处先缓存的作用是,为了避免可能会在加锁代码块内造成的ALooper释放之后,
// 其析构函数被执行时会再次请求该锁而造成死锁问题。
// 而使用vector加锁代码块外部变量持有其强引用对象,
// 那么则会在跳出当前加锁代码块即已经释放锁之后,
// 再可能执行ALooper析构函数时则不会造成死锁。
// At this point 'looper' might be the only sp<> keeping
// the object alive. To prevent it from going out of scope
// and having ~ALooper call this method again recursively
// and then deadlocking because of the Autolock above, add
// it to a Vector which will go out of scope after the lock
// has been released.
activeLoopers.add(looper);
}
}
}
}
mLooper->setName(“MediaClock”)实现分析:
设置ALooper消息循环线程名
// [frameworks/av/media/libstagefright/foundation/ALooper.cpp]
void ALooper::setName(const char *name) {
mName = name;
}
mLooper->start()实现分析:
启动消息循环线程
// [frameworks/av/media/libstagefright/foundation/ALooper.cpp]
status_t ALooper::start(
bool runOnCallingThread, bool canCallJava, int32_t priority) {
// 注意 [runOnCallingThread] 该参数的作用,
// 根据名称可知,是否运行在调用者线程中,
// 其实就是根据该设置值来选择运行当前线程循环方法的线程,
// 即在哪个线程中运行消息循环线程方法
if (runOnCallingThread) {
// 为true时,调用者线程中运行消息循环方法
{
// 加锁代码块
Mutex::Autolock autoLock(mLock);
// 判断ALooper线程是否存在,或者是否本地(线程)运行即在调用者线程中运行
if (mThread != NULL || mRunningLocally) {
// 若已创建本地新线程则不允许再次在调用者线程中运行消息循环方法,或者已在本地线程中运行时。
// 其实就是判断消息循环方法是否已执行过,已执行过则返回无效操作状态
return INVALID_OPERATION;
}
// 标记已在本地线程中运行标志位
mRunningLocally = true;
}
// 执行消息循环方法,根据返回值来判断是否应该循环执行该方法
// loop() 方法实现见1.1小节分析
do {
} while (loop());
// 返回成功
return OK;
}
// 为false时,创建新线程来运行消息循环方法
// 加锁
Mutex::Autolock autoLock(mLock);
if (mThread != NULL || mRunningLocally) {
// 若已创建本地新线程则不允许再次在调用者线程中运行消息循环方法,或者已在本地线程中运行时。
// 其实就是判断消息循环方法是否已执行过,已执行过则返回无效操作状态
return INVALID_OPERATION;
}
// 创建消息循环新线程
// LooperThread类声明和构造函数初始化,见1.2小节分析
mThread = new LooperThread(this, canCallJava);
// LooperThread run()方法,见1.3小节分析
status_t err = mThread->run(
mName.empty() ? "ALooper" : mName.c_str(), priority);
// 若启动消息循环线程失败,则清除(释放)该线程指针
if (err != OK) {
mThread.clear();
}
return err;
}
1.1、loop() 实现分析
消息循环处理方法
// [frameworks/av/media/libstagefright/foundation/ALooper.cpp]
bool ALooper::loop() {
// 事件消息对象
Event event;
// 加锁代码块 获取消息队列中的消息
{
Mutex::Autolock autoLock(mLock);
if (mThread == NULL && !mRunningLocally) {
// 由前面的可知,此处表示消息循环线程功能还未启动,则不处理
return false;
}
// 判断消息队列是否为空
if (mEventQueue.empty()) {
// 若为空,则执行消息队列变化条件变量等待唤醒锁。
// 注:刚开始启动ALooper时会进入到此处挂起调用者或消息循环线程,
// 等待其他线程传入消息后唤醒。
mQueueChangedCondition.wait(mLock);
return true;
}
// 获取消息队列中第一个数据的执行时间点【微秒】
// 注:消息添加时已按照执行时间点先后顺序排序,见后面的添加消息分析
int64_t whenUs = (*mEventQueue.begin()).mWhenUs;
// 获取当前系统时间点
int64_t nowUs = GetNowUs();
// 判断执行时间点是否大于当前时间点
if (whenUs > nowUs) {
// 大于时,则计算其(延迟执行的)延迟时间
int64_t delayUs = whenUs - nowUs;
// 若延迟时间大于64位int最大值转换的微秒值,
// 则取该最大值,不应该超过该值
if (delayUs > INT64_MAX / 1000) {
delayUs = INT64_MAX / 1000;
}
// 条件变量等待延迟时间时长后才再次自动唤醒此处并重新消息循环执行,
// 传入时间单位为纳秒 ns 【因此上面检查必现不能大于int最大值】
mQueueChangedCondition.waitRelative(mLock, delayUs * 1000ll);
return true;
}
// 若小于等于时,则表明轮到当前事件消息执行
// 从消息队列中取第一个事件消息
event = *mEventQueue.begin();
// 从消息队列中清除该已取出的消息对象
mEventQueue.erase(mEventQueue.begin());
}
// 上面代码块加锁主要就是操作共享数据获取事件消息,此处已释放该锁
// 执行当前事件对象中该消息的发送消息方法
// 见下面AMessage相关小节中的分析
event.mMessage->deliver();
// NOTE: It's important to note that at this point our "ALooper" object
// may no longer exist (its final reference may have gone away while
// delivering the message). We have made sure, however, that loop()
// won't be called again.
return true;
}
GetNowUs()实现:
获取当前系统时间,单位为微秒 Us
// [frameworks/av/media/libstagefright/foundation/ALooper.cpp]
// static
int64_t ALooper::GetNowUs() {
return systemTime(SYSTEM_TIME_MONOTONIC) / 1000LL;
}
1.2、LooperThread类声明和构造函数初始化
LooperThread类声明:
它的类声明其实是ALooper的内部类声明方式
// [frameworks/av/media/libstagefright/foundation/include/media/stagefright/foundation/ALooper.h]
struct ALooper : public RefBase {
private:
struct LooperThread;
}
LooperThread类所有相关实现:
// [frameworks/av/media/libstagefright/foundation/ALooper.cpp]
struct ALooper::LooperThread : public Thread {
LooperThread(ALooper *looper, bool canCallJava)
//调用了父类Thread的构造函数
: Thread(canCallJava),
// 缓存looper变量
mLooper(looper),
// 线程ID初始化NULL
mThreadId(NULL) {
}
// 覆写父类该方法,该方法由Thread章节分析可知,在第一次线程运行时只会执行一次
virtual status_t readyToRun() {
// Android获取线程内核ID,该方法实现请查看我的native Thread源码分析章节
mThreadId = androidGetThreadId();
// 然后调用父类该方法,其实父类一直返回OK
return Thread::readyToRun();
}
// 覆写父类方法,该方法即为线程循环执行方法,可参考Thread源码分析章节
virtual bool threadLoop() {
// 由此处可知,最终线程循环执行到了ALooper对象的loop()循环方法,即上面已分析的
return mLooper->loop();
}
// 声明和定义新方法,判断当前线程是否和消息循环运行线程是同一个线程
bool isCurrentThread() const {
return mThreadId == androidGetThreadId();
}
protected:
// 析构函数空实现
virtual ~LooperThread() {}
private:
ALooper *mLooper;
android_thread_id_t mThreadId;
// 宏定义,见类图关系中的标注,不用分析
DISALLOW_EVIL_CONSTRUCTORS(LooperThread);
};
1.3、LooperThread run()方法实现分析
由1.2小节分析可知,该循环线程类继承Thread类,再根据Thread类源码分析可知,该run()方法就是Thread类的方法,其实际就是启动线程循环功能,使其最终会执行 readyToRun() 和threadLoop() 方法。
见另一章节分析 【Android native/C++层Thread线程实现源码分析】
2、mLooper->registerHandler(this) 实现分析:
向ALooper中注册AHandler。
首先分析AHandler类声明和及其实现:
// [frameworks/av/media/libstagefright/foundation/include/media/stagefright/foundation/AHandler.h]
struct AHandler : public RefBase {
AHandler()
// 一个Handler id值,默认为0
: mID(0),
// 该字段作用:是否需要记录AMessage消息统计列表信息并可更新,
// true即需要,默认为false即不需要记录
mVerboseStats(false),
// 消息计数器(记录已接收到的消息总个数),默认0
mMessageCounter(0) {
}
// 注意以下几个 const 修饰方法
// 返回当前Handler id值(其实际是个int类型别名,见ALooper中声明)
ALooper::handler_id id() const {
return mID;
}
// 获取ALooper时需要将弱引用指针对象尝试提升为强引用指针对象,可能为空
// 【当ALooper的所有强引用计数为0即该对象指针已被释放时】
sp<ALooper> looper() const {
return mLooper.promote();
}
// 该方法获取的是缓存的ALooper弱指针引用对象
wp<ALooper> getLooper() const {
return mLooper;
}
// 获取当前AHandler对象的弱指针引用对象
wp<AHandler> getHandler() const {
// allow getting a weak reference to a const handler
// 通过强转为const AHandler弱指针引用对象
return const_cast<AHandler *>(this);
}
protected:
// 该方法即为子类需要覆写实现来接收事件消息的虚函数方法
virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
private:
// 定义友元类
friend struct AMessage; // deliverMessage()
friend struct ALooperRoster; // setID()
ALooper::handler_id mID;
// 缓存ALooper的弱指针引用
wp<ALooper> mLooper;
// 内联函数,设置当前handler id值和ALooper
inline void setID(ALooper::handler_id id, const wp<ALooper> &looper) {
mID = id;
mLooper = looper;
}
bool mVerboseStats;
uint32_t mMessageCounter;
// 消息统计集合,但注意该消息统计是记录当前某一事件最新的事件消息数据,即以key为唯一值来操作的,
// 但该统计功能默认不开启,只由当【mVerboseStats】为true时才开启
KeyedVector<uint32_t, uint32_t> mMessages;
// 发送消息
// 见下面的分析
void deliverMessage(const sp<AMessage> &msg);
DISALLOW_EVIL_CONSTRUCTORS(AHandler);
}
deliverMessage() 实现分析
// [frameworks/av/media/libstagefright/foundation/AHandler.cpp]
void AHandler::deliverMessage(const sp<AMessage> &msg) {
// 执行该方法即子类需要覆写实现来接收事件消息的虚函数方法
onMessageReceived(msg);
// 计数以接收消息总个数
mMessageCounter++;
// 判断是否需要统计最新事件消息,该值默认为false
if (mVerboseStats) {
// 通过Key值来唯一标识关联最新的AMessage消息对象
uint32_t what = msg->what();
ssize_t idx = mMessages.indexOfKey(what);
// 小于0表示不包含则添加新事件消息,否在更新它
if (idx < 0) {
mMessages.add(what, 1);
} else {
mMessages.editValueAt(idx)++;
}
}
}
继承AHandler实现的子类【此处直接分析MediaClock】
// [frameworks/av/media/libstagefright/include/media/stagefright/MediaClock.h]
struct MediaClock : public AHandler {
// 接收事件消息方法
virtual void onMessageReceived(const sp<AMessage> &msg);
private:
sp<ALooper> mLooper;
}
registerHandler() 方法实现分析:
// [frameworks/av/media/libstagefright/foundation/ALooper.cpp]
ALooper::handler_id ALooper::registerHandler(const sp<AHandler> &handler) {
return gLooperRoster.registerHandler(this, handler);
}
// [frameworks/av/media/libstagefright/foundation/ALooperRoster.cpp]
ALooper::handler_id ALooperRoster::registerHandler(
const sp<ALooper> &looper, const sp<AHandler> &handler) {
// 加锁(添加数据)
Mutex::Autolock autoLock(mLock);
// 由上面的AHandler部分分析,可知,handler id 默认为0,
// 若不为0则表示已经注册过了,每个AHandler的ID只能设置一次
if (handler->id() != 0) {
CHECK(!"A handler must only be registered once.");
return INVALID_OPERATION;
}
// 缓存Handler信息项即记录ALooper和AHandler对应关联项
HandlerInfo info;
info.mLooper = looper;
info.mHandler = handler;
// 获取当前的handler id,其实际上就一个自增的int类型值,
// 由前面分析可知该值初始化为1,因此若是第一个handler注册,
// 那么该handler id将为1,然后再自增加1为下一个handler id。
// 注意:mNextHandlerID++ 这个运算符的作用,先使用它的值,然后才加1
ALooper::handler_id handlerID = mNextHandlerID++;
// 将其添加到mHandlers的K-V映射记录列表中,相当于java的map对象作用
mHandlers.add(handlerID, info);
// 然后调用AHandler对象的setID方法,将handler id和ALooper设置给当前Ahanlder
handler->setID(handlerID, looper);
// 然后返回当前handler id值
return handlerID;
}
3、发送消息:
ALooper内部线程或其他线程中均可发送消息。
由于本章节篇幅过长,因此将后续分析部分放在本系列下一章节分析,请查看:
Android native层媒体通信架构AHandler/ALooper机制实现源码分析【Part 2】