Android 12(S) ALooper AHandler AMessage(一)
卧榻之侧岂容他人酣睡,到现在ALooper AHandler AMessage的工作原理一直都没搞懂,很慌!看他们的路径都在libstagefright/foundation下,作为一个foundation怎么能不去搞明白,今天必须解决他们!
相关代码路径:
从哪里开始?就从他们怎么使用开始!然后顺着看
(一)正常使用这三剑客时,会先去创建一个ALooper以及AHandler,接着把AHandler注册到ALooper当中。
1、ALooper构造函数
ALooperRoster gLooperRoster;
ALooper::ALooper()
: mRunningLocally(false) {
gLooperRoster.unregisterStaleHandlers();
}
从这里可以看到ALooper中维护了一个全局变量gLooperRoster
void ALooperRoster::unregisterStaleHandlers() {
Vector<sp<ALooper> > activeLoopers;
{
Mutex::Autolock autoLock(mLock);
for (size_t i = mHandlers.size(); i > 0;) {
i--;
const HandlerInfo &info = mHandlers.valueAt(i);
sp<ALooper> looper = info.mLooper.promote();
if (looper == NULL) {
ALOGV("Unregistering stale handler %d", mHandlers.keyAt(i));
mHandlers.removeItemsAt(i);
} else {
activeLoopers.add(looper);
}
}
}
}
unregisterStaleHandlers方法用来移除已经被释放的Alooper
2、AHandler构造函数
AHandler()
: mID(0),
mVerboseStats(false),
mMessageCounter(0) {
}
这个构造函数显得更为简单,初始化了Ahandler的id信息mID,以及持有的message的数量mMessageCounter
3、给ALooper注册Ahandler
ALooper::handler_id ALooper::registerHandler(const sp<AHandler> &handler) {
return gLooperRoster.registerHandler(this, handler);
}
将ALooper自身和AHandler绑定,一并交给ALooperRoster来管理
ALooper::handler_id ALooperRoster::registerHandler(
const sp<ALooper> &looper, const sp<AHandler> &handler) {
Mutex::Autolock autoLock(mLock); if (handler->id() != 0) {
CHECK(!"A handler must only be registered once.");
return INVALID_OPERATION;
} HandlerInfo info;
info.mLooper = looper;
info.mHandler = handler;
ALooper::handler_id handlerID = mNextHandlerID++;
mHandlers.add(handlerID, info); handler->setID(handlerID, looper); return handlerID;
} KeyedVector<ALooper::handler_id, HandlerInfo> mHandlers;
ALooperRoster::registerHandler做了三件事:
a. 将ALooper和AHandler打包为一个HandlerInfo
b. 用一个递增的id mNextHandlerID,将HandlerInfo以KeyedVector的形式管理起来
c. 调用AHandler的setID方法,更新AHandler的id,通知AHandler持有它的ALooper是谁
在反过来看这个方法的一开头判断AHandler的id是否为0,这个就是用来防止AHandler被注册到多个ALooper当中去,意思就是一个ALooper可以有多个AHandler,但是一个AHandler不能注册到多个ALooper中去
inline void setID(ALooper::handler_id id, const wp<ALooper> &looper) {
mID = id;
mLooper = looper;
}
4、让ALooper开始打工 start
status_t ALooper::start(
bool runOnCallingThread, bool canCallJava, int32_t priority) {
if (runOnCallingThread) {
{
Mutex::Autolock autoLock(mLock); if (mThread != NULL || mRunningLocally) {
return INVALID_OPERATION;
} mRunningLocally = true;
} do {
} while (loop()); return OK;
} Mutex::Autolock autoLock(mLock); if (mThread != NULL || mRunningLocally) {
return INVALID_OPERATION;
} mThread = new LooperThread(this, canCallJava); status_t err = mThread->run(
mName.empty() ? "ALooper" : mName.c_str(), priority);
if (err != OK) {
mThread.clear();
} return err;
}
这里的参数有点奇怪,让我看看NuPlayer里怎么用的
mRendererLooper = new ALooper;
mRendererLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
a. 第一个参数看起来是确定ALooper是否在调用线程中工作,
b. 第二个参数从名字上来看意为是否能调用java方法
c. 第三个参数为优先级,会传进Thread当中
下面来看start的内部实现,实现根据runOnCallingThread分为两种情况:
a. 例子中NuPlayer填的false,所以创建了一个LooperThread对象,执行其run方法开启线程(该方法在基类Thread当中)
Thread类:system\core\libutils\Threads.cpp,这里不做研究
virtual bool threadLoop() {
return mLooper->loop();
}
最后应该会在一个新的线程中执行loop方法
b. 如果runOnCallingThread为true,那么就会在当前线程执行loop方法
bool ALooper::loop() {
Event event;
{
Mutex::Autolock autoLock(mLock);
if (mThread == NULL && !mRunningLocally) {
return false;
}
if (mEventQueue.empty()) {
mQueueChangedCondition.wait(mLock);
return true;
}
int64_t whenUs = (*mEventQueue.begin()).mWhenUs;
int64_t nowUs = GetNowUs();
if (whenUs > nowUs) {
int64_t delayUs = whenUs - nowUs;
if (delayUs > INT64_MAX / 1000) {
delayUs = INT64_MAX / 1000;
}
mQueueChangedCondition.waitRelative(mLock, delayUs * 1000ll);
return true;
}
event = *mEventQueue.begin();
mEventQueue.erase(mEventQueue.begin());
}
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;
}
仔细推究的话这里会有一个问题,如果在当前线程执行loop函数不会阻塞主线程吗?其实不会的,这里利用到了条件变量,执行到mQueueChangedCondition.wait时这个循环就会休眠,等待signal触发再运行,具体可以查看pthread_cond_t的工作原理。
(二)接下来就要看看他们传递处理的对象AMessage,AMessage可以只当作信息的载体,也可以作为异步信号
1、AMessage构造函数
AMessage::AMessage(void)
: mWhat(0),
mTarget(0) {
} AMessage::AMessage(uint32_t what, const sp<const AHandler> &handler)
: mWhat(what) {
setTarget(handler);
}
AMessage有两种构造函数,
a. 无参构造更加适合于对象只当信息载体,保存数据
b. 有参构造函数适合作为一个信号,用于程序间的异步调用。当然也可以先创建一个无参对象,再去调用setTarget设定处理它的AHandler
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();
}
}
从setTarget方法可以看到AMessage会持有target AHanlder以及对应的ALooper对象
2、发送消息
有三种发送消息的方式,接下来一个个看:
a. post
status_t AMessage::post(int64_t delayUs) {
sp<ALooper> looper = mLooper.promote();
if (looper == NULL) {
ALOGW("failed to post message as target looper for handler %d is gone.", mTarget);
return -ENOENT;
}
looper->post(this, delayUs);
return OK;
}
看到AMessage::post中调用的是ALooper::post (ALooper::post作为一个私有方法在AMessage中可以调用是因为ALooper定义AMessage是其友元)
void ALooper::post(const sp<AMessage> &msg, int64_t delayUs) {
Mutex::Autolock autoLock(mLock);
// 计算什么时候post消息
int64_t whenUs;
if (delayUs > 0) {
int64_t nowUs = GetNowUs();
whenUs = (delayUs > INT64_MAX - nowUs ? INT64_MAX : nowUs + delayUs);
} else {
whenUs = GetNowUs();
}
List<Event>::iterator it = mEventQueue.begin();
while (it != mEventQueue.end() && (*it).mWhenUs <= whenUs) {
++it;
}
Event event;
event.mWhenUs = whenUs;
event.mMessage = msg;
if (it == mEventQueue.begin()) {
mQueueChangedCondition.signal();
}
mEventQueue.insert(it, event);
}
在这里看到会将消息打包为Event 插入到ALooper的队列当中,同时通知loop循环开始工作;loop方法会调用AMessage的deliver方法(同样AMessage也是AHandler的友元)
void AMessage::deliver() {
sp<AHandler> handler = mHandler.promote();
if (handler == NULL) {
ALOGW("failed to deliver message as target handler %d is gone.", mTarget);
return;
}
handler->deliverMessage(this);
}
void AHandler::deliverMessage(const sp<AMessage> &msg) {
onMessageReceived(msg);
mMessageCounter++;
if (mVerboseStats) {
uint32_t what = msg->what();
ssize_t idx = mMessages.indexOfKey(what);
if (idx < 0) {
mMessages.add(what, 1);
} else {
mMessages.editValueAt(idx)++;
}
}
}
deliverMessage是AHandler.cpp中唯一的方法,其调用的核心方法是onMessageReceived,这是一个纯虚函数,需要一个子类来实现!onMessageReceived这里就处理结束了
b. postAndAwaitResponse
从名字上来看,发出消息之后需要等待处理完成之后并返回结果
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);
}
通过ALooper创建一个AReplyToken,并且把这个token加入到AMessage当中,接着post出去,到这里为止,调用的过程和直接基本相同,不同的是这里会最后会调用awaitResponse方法等待返回一个结果。
这个结果如何返回呢?那就是调用与其相对应的方法postReply,意思就是在调用完成后会创建一个新的AMessage保存结果,然后根据token将这个结果返回给调用者
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);
}
postReply方法调用了ALooper的postReply方法
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;
}
调用replyToken的setReply方法将返回的结果保存到token当中,这样ALooper的awaitResponse方法可以拿回这个结果,这样一次完整的调用就结束了。
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;
}
这里还有一个方法要看,在onMessageReceived方法中要如何拿到送过来的AReplyToken呢?AMessage给我们提供了一个方法:senderAwaitsResponse
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;
}
就是利用的findObject方法来查找的。
这三个方法postAndAwaitResponse、postReply、senderAwaitsResponse需要共同协作可以完成一次异步阻塞调用。
其他的AMessage中的方法可以直接去AMessage.cpp中查询。

最后再贴一张时序图。
为什么post一条AMessage需要搞得这么复杂呢?
我觉得是:一个ALooper管理着多个AHandler,起着事件的存储和分发作用,为了让AMessage能够找到正确的AHandler处理,需要调用AMessage自身的deliver方法找到对应的AHandler。
从图上来看AHandler和ALooper没有什么关系,为什么创建handler之后要调用registerHandler呢?
我觉得是:创建AMessage之后,要去setTarget(也就是handler),这时候handler中也保存了Looper对象,调用AMessage的post方法时就能找到正确的Looper来处理了
Android 12(S) ALooper AHandler AMessage(一)的更多相关文章
- Android 12(S) 图形显示系统 - 示例应用(二)
1 前言 为了更深刻的理解Android图形系统抽象的概念和BufferQueue的工作机制,这篇文章我们将从Native Level入手,基于Android图形系统API写作一个简单的图形处理小程序 ...
- Android 12(S) 图形显示系统 - 基本概念(一)
1 前言 Android图形系统是系统框架中一个非常重要的子系统,与其它子系统一样,Android 框架提供了各种用于 2D 和 3D 图形渲染的 API供开发者使用来创建绚丽多彩的应用APP.图形渲 ...
- Android 12(S) 图形显示系统 - 应用建立和SurfaceFlinger的沟通桥梁(三)
1 前言 上一篇文章中我们已经创建了一个Native示例应用,从使用者的角度了解了图形显示系统API的基本使用,从这篇文章开始我们将基于这个示例应用深入图形显示系统API的内部实现逻辑,分析运作流程. ...
- Android 12(S) 图形显示系统 - SurfaceFlinger的启动和消息队列处理机制(四)
1 前言 SurfaceFlinger作为Android图形显示系统处理逻辑的核心单元,我们有必要去了解其是如何启动,初始化及进行消息处理的.这篇文章我们就来简单分析SurfaceFlinger这个B ...
- Android 12(S) 图形显示系统 - createSurface的流程(五)
题外话 刚刚开始着笔写作这篇文章时,正好看电视在采访一位92岁的考古学家,在他的日记中有这样一句话,写在这里与君共勉"不要等待幸运的降临,要去努力的掌握知识".如此朴实的一句话,此 ...
- Android 12(S) 图形显示系统 - BufferQueue/BLASTBufferQueue之初识(六)
题外话 你有没有听见,心里有一声咆哮,那一声咆哮,它好像在说:我就是要从后面追上去! 写文章真的好痛苦,特别是自己对这方面的知识也一知半解就更加痛苦了.这已经是这个系列的第六篇了,很多次都想放弃了,但 ...
- Android 12(S) 图形显示系统 - 初识ANativeWindow/Surface/SurfaceControl(七)
题外话 "行百里者半九十",是说步行一百里路,走过九十里,只能算是走了一半.因为步行越接近目的地,走起来越困难.借指凡事到了接近成功,往往是最吃力.最艰难的时段.劝人做事贵在坚持, ...
- Android 12(S) 图形显示系统 - BufferQueue的工作流程(八)
题外话 最近总有一个感觉:在不断学习中,越发的感觉自己的无知,自己是不是要从"愚昧之巅"掉到"绝望之谷"了,哈哈哈 邓宁-克鲁格效应 一.前言 前面的文章中已经 ...
- Android 12(S) 图形显示系统 - BufferQueue的工作流程(九)
题外话 Covid-19疫情的强烈反弹,小区里检测出了无症状感染者.小区封闭管理,我也不得不居家办公了.既然这么大把的时间可以光明正大的宅家里,自然要好好利用,八个字 == 努力工作,好好学习 一.前 ...
- Android 12(S) 图形显示系统 - BufferQueue的工作流程(十)
题外话 疫情隔离在家,周末还在努力学习的我 ..... 一.前言 上一篇文章中,有基本讲清楚Producer一端的处理逻辑,最后也留下了一个疑问: Consumer是什么时候来消费数据的?他是自己主 ...
随机推荐
- 活动开启 | 以梦筑码 · 不负韶华 开发者故事征集令,讲出你的故事,有机会参加HDC.Together 2023
HarmonyOS面世以来,经历了3大版本迭代,系统能力逐步完善,生态加速繁荣.一路前行,是开发者们点亮漫天星光.点滴贡献,聚沙成塔,开发者们正用代码改变世界. 是梦想,激励我们一路前行.在黎明到 ...
- Unable to determine application id: com.android.tools.idea.run.ApkProvisionException: No outputs for the main artifact of variant: debug
前言 遇到这种情况极大可能属于gradle 与 android studio版本不匹配的情况. 因为我是在升级android studio遇到的这个问题. 方法 一个方法是:降低android stu ...
- WPF随笔收录-RestSharp下载文件406问题
一.前言 在项目开发过程中,涉及到通过http下载文件的需求,最近遇到一个406问题,由于第一次接触这个问题,也被问题卡了好久,在网上风暴了很久才找到解决办法: 二.解决方法 解决的办法就是在requ ...
- GaussDB(DWS)基于Flink的实时数仓构建
本文分享自华为云社区<GaussDB(DWS)基于Flink的实时数仓构建>,作者:胡辣汤. 大数据时代,厂商对实时数据分析的诉求越来越强烈,数据分析时效从T+1时效趋向于T+0时效,为了 ...
- Mac 上fiddler与charles 抓包https 小程序请求 内容
为什么选择charles 之前讲过<wireshark使用教程及过滤语法总结--血泪史的汇聚>, 很强大,但是很难用. fiddler 很好用,之前mac 上面没有,现在有了 fiddle ...
- 剑指offer66(Java)-构建乘积数组(中等)
题目: 给定一个数组 A[0,1,-,n-1],请构建一个数组 B[0,1,-,n-1],其中 B[i] 的值是数组 A 中除了下标 i 以外的元素的积, 即 B[i]=A[0]×A[1]×-×A[i ...
- 力扣69(java&python)-x的平方根(简单)
题目: 给你一个非负整数 x ,计算并返回 x 的 算术平方根 . 由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 . 注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0. ...
- 【pytorch学习】之数据预处理
2 数据预处理 为了能用深度学习来解决现实世界的问题,我们经常从预处理原始数据开始,而不是从那些准备好的张量格式数据开始.在Python中常用的数据分析工具中,我们通常使用pandas软件包.像庞大的 ...
- 好代码实践:基于Redis的轻量级分布式均衡消费队列
简介: 好代码,给人第一个印象的感觉,就像一篇好文章一样,读起来朗朗上口.不同的文章有不同的风格体裁,不同的代码也有不同的编程风格要求.Python有严格的缩进,像诗歌一样工整对仗:C语言面向过程像散 ...
- [FE] uni-app 动态改变 navigationBarTitleText 导航标题
改导航文字: uni.setNavigationBarTitle({ title: 'xx' }); 改 tabBar 文字: uni.setTabBarItem({ index: 0, text: ...