AHandler AMessage ALooper消息机制

AHandler是Android native层实现的一个异步消息机制,在这个机制中所有的处理都是异步的,将变量封装到一个消息AMessage结构体中,然后放到队列中去,后台专门有一个线程ALooper会从这个队列中取出消息然后分发执行,执行函数就是AHandler实例的onMessageReceived

注意:本文中源码是基于Android11的,6.0以上都是相同的,6.0以下的源码略有不同

1.主要类

主要的涉及的有以下三个类

  • AMessage

    消息类,用于构造消息,通过post方法投递出去给ALooper

  • ALooper

    轮询类,用于保存消息队列,然后将消息发送到对应的AHandler

  • AHandler

    处理类,用于实际消息的处理

2.消息类型

发送的消息按照是否需要答复,分为两种

  1. 普通消息
  2. 需答复消息

3.模型简介

普通消息被Post到消息队列,Looper不断的从队列中取出消息并分发给相应的Handler处理。如果是需答复消息的话使用PostAndAwaitResponseHandler处理完以后通过postReply将消息返回给发送方,发送方通过新建的responsemsg接收。

4.代码分析

4.1 初始化

伪代码

sp<ALooper> looper = new ALooper;//创建一个ALooper实例
sp<AHandlerReflector> mHandler = new AHandlerReflector();//创建一个AHandler实例
looper->registerHandler(mHandler);//1. registerHandler会调用AHandler的setID方法将looper设置到Handler里面
looper->start(true);//2. 根据参数创建并启动LooperThread

代码追踪

  1. 注册mHandler到looper

    ALooper.cpp
ALooper::handler_id ALooper::registerHandler(const sp<AHandler> &handler) {
return gLooperRoster.registerHandler(this, handler);
}

直接调用ALooperRosterregisterHandler,生成一个handler ID并保存起来

ALooperRoster.cpp

  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."); //一个handler在同一个looper中只能注册一次
return INVALID_OPERATION;
} HandlerInfo info; //HandlerInfo 结构体,保存looper和handler,将其绑定在一起
info.mLooper = looper;
info.mHandler = handler;
ALooper::handler_id handlerID = mNextHandlerID++; //生成handler ID
mHandlers.add(handlerID, info); //mHandlers为ALooperRoster的私有,保存handler列表 handler->setID(handlerID, looper); return handlerID;
}
  1. 启动looper循环

    ALooper.cpp
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;
}

4.2 普通消息使用流程

  1. 实现一个AHandler的子类,主要是onMessageReceived方法
  2. 创建一个ALooper对象
  3. 创建消息AMessage进行投递Post
  4. onMessageReceived方法得到调用,处理实际的事务

伪代码

发送

sp<AMessage> msg = new AMessage(kWhatSaySomething, mHandler); //在AMessage的构造方法里获取Ahandler对应的Looper并保存
msg->post(); //1. 发送消息

处理

AHandlerReflector:: onMessageReceived //消息的具体的处理实现

代码追踪

发送消息

AMessage.cpp

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); //进到looper线程
return OK;
}

ALooper.cpp

void ALooper::post(const sp<AMessage> &msg, int64_t delayUs) {
Mutex::Autolock autoLock(mLock); int64_t whenUs;
if (delayUs > 0) {
whenUs = GetNowUs() + 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(); //如果之前消息队列为空,没有event,loop是处于阻塞状态的,此处当有消息进入时,先通知looperthread启动
} mEventQueue.insert(it, event); //进入消息队列
}

looper循环进行分发

ALooper.cpp

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;
mQueueChangedCondition.waitRelative(mLock, delayUs * 1000ll); return true;
} event = *mEventQueue.begin(); //取出队列排头消息
mEventQueue.erase(mEventQueue.begin()); //退出消息队列
} event.mMessage->deliver(); //根据每个消息携带的handler做转发处理 // 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;
}

返回工作线程

AMessage.cpp

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); //转发到相应的handler实例
}

转发到相应的Handler

void AHandler::deliverMessage(const sp<AMessage> &msg) {
onMessageReceived(msg);//Handler实例必须覆盖此方法,在这里做对应的处理,一般采用switch语句进行不同的处理
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)++;
}
}
}

4.3 需要答复的消息使用流程

  1. 实现一个AHandler的子类,主要是onMessageReceived方法
  2. 创建一个ALooper对象
  3. 创建消息AMessage进行投递PostAndAwaitResponse
  4. onMessageReceived方法得到调用,处理实际的事务
  5. 处理完消息后发送答复postReply
  6. 发送端接收答复消息response

伪代码

发送/接收答复

sp<AMessage> msg = new AMessage(kWhatSaySomething, mHandler); //发送的消息
sp<AMessage> responsemsg; //接收存储答复消息
msg->postAndAwaitResponse(responsemsg);

处理/答复

AHandlerReflector:: onMessageReceived //消息的具体的处理实现
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
sp<AMessage> response = new AMessage;//创建答复消息
response->postReply(replyID);//发送答复消息

代码追踪

AMessage.cpp

status_t AMessage::postAndAwaitResponse(sp<AMessage> *response) {
return gLooperRoster.postAndAwaitResponse(this, response);
}

AMessage.cpp

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(); 创建Token标识,此token用来唯一的标识这条信息
if (token == NULL) {
ALOGE("failed to create reply token");
return -ENOMEM;
}
setObject("replyID", token); //将此token添加到该Message的replyID字段 looper->post(this, 0 /* delayUs */); //发给looper
return looper->awaitResponse(token, response); //在此处就阻塞住,不返回了
}

post过程和普通消息的一致,先发送到looper,然后loop分发给对应的handler处理,不同的是此处在mRepliesCondition.wait(mRepliesLock)阻塞等待

ALooper.cpp

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

等收到mRepliesCondition.broadcast()以后,会执行retrieveReply,它的作用是将返回的reply拷贝到response

AReplyToken是一个结构体,定义在AMessage.h文件中

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

所以上面的程序就在等待mRepliesCondition.broadcast()的发出,而这个broadcast是在哪里发出了,我们接着看

接着看消息的处理,和普通消息一致,在相应的handler实例的onMessageReceived中得到处理,处理完以后创建答复消息

sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
sp<AMessage> response = new AMessage;//创建答复消息
response->postReply(replyID);//发送答复消息

答复消息也是也AMessage的形式发出

AMessage.cpp

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();//根据replyToken获取消息所在的looper
if (looper == NULL) {
ALOGW("failed to post reply as target looper is gone.");
return -ENOENT;
}
return looper->postReply(replyToken, this); //转到looper
}

looper这里首先将reply消息拷贝的位于AReplyToken的一个缓冲变量mReply,然后发出回复成果广播

ALooper.cpp

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

保存回复信息到缓存变量mReply

AMessage.cpp

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

参考:

AHandler AMessage ALooper消息机制

AHandler AMessage ALooper消息机制-源码6.0.0以上的更多相关文章

  1. Android Handler消息机制源码解析

    好记性不如烂笔头,今天来分析一下Handler的源码实现 Handler机制是Android系统的基础,是多线程之间切换的基础.下面我们分析一下Handler的源码实现. Handler消息机制有4个 ...

  2. Android消息机制源码分析

    本篇主要介绍Android中的消息机制,即Looper.Handler是如何协同工作的: Looper:主要用来管理当前线程的消息队列,每个线程只能有一个Looper Handler:用来将消息(Me ...

  3. [Android]简略的Android消息机制源码分析

    相关源码 framework/base/core/java/andorid/os/Handler.java framework/base/core/java/andorid/os/Looper.jav ...

  4. 史上最详细的Android消息机制源码解析

    本人只是Android菜鸡一个,写技术文章只是为了总结自己最近学习到的知识,从来不敢为人师,如果里面有不正确的地方请大家尽情指出,谢谢! 606页Android最新面试题含答案,有兴趣可以点击获取. ...

  5. Android事件分发机制源码分析

    Android事件分发机制源码分析 Android事件分发机制源码分析 Part1事件来源以及传递顺序 Activity分发事件源码 PhoneWindow分发事件源码 小结 Part2ViewGro ...

  6. Springboot学习04-默认错误页面加载机制源码分析

    Springboot学习04-默认错误页面加载机制源码分析 前沿 希望通过本文的学习,对错误页面的加载机制有这更神的理解 正文 1-Springboot错误页面展示 2-Springboot默认错误处 ...

  7. 阿里云视频直播API签名机制源码

    阿里云视频直播API签名机制源码 本文展示:通过代码实现下阿里视频直播签名处理规则 阿里云视频直播签名机制,官方文档链接:https://help.aliyun.com/document_detail ...

  8. ApplicationEvent事件机制源码分析

    <spring扩展点之三:Spring 的监听事件 ApplicationListener 和 ApplicationEvent 用法,在spring启动后做些事情> <服务网关zu ...

  9. 【转】Win 7 下源码运行OpenERP7.0

    原文地址:Win 7 下源码运行OpenERP7.0 安装Python2.7 下载地址:http://www.python.org/getit/注:OpenERP7.0支持的Python版本最高为2. ...

  10. CentOS7源码安装Redis5.0.4非关系型数据库

    源码安装redis-5.0.4 一. 下载redis 1. 需要连接网络 二. 案例(另一种安装方法) [root@localhost ~]# wget http://download.redis.i ...

随机推荐

  1. CF1764H Doremy's Paint 2 题解

    题目链接:CF 或者 洛谷 高分题,感觉挺有意思的题,值得一提的是这个题的 \(1\) 和 \(3\) 版本却是两个基础题. 一开始以为跟这道差不多:P8512 [Ynoi Easy Round 20 ...

  2. YOLO数据集划分(测试集和验证集)

    在目标检测任务中,数据集的划分通常分为训练集和验证集,以便在训练模型时评估模型的性能.这个过程对于有效训练和评估目标检测模型非常重要.下面是划分目标检测数据集的一般步骤:`` 数据集组织: 确保你的数 ...

  3. Oracle-报错信息显示问号或者乱码(Oracle 19c)

    问题描述: 通过sqlplus登录Oracle19c时,执行SQL出现报错时,显示错误码出现问号: 解决方法: su   -  oracle vi .bash_profile 添加如下一行内容: ex ...

  4. Winows11-hosts文件无法修改保存

    Win11系统hosts文件无法修改保存 新近使用win11新电脑修改hosts,添加IP和主机名映射,保存时提示host无法修改. 解决办法: 1.将hosts文件的权限"只读" ...

  5. 2023牛客暑期多校训练营3 ABDHJ

    比赛链接 A 题解 知识点:数学. 当 \(x = 0\) 时,当且仅当 \(y = 0\) 可行. 当 \(x \neq 0\) 时,一定可行,答案为 \(|x-y|\) . 时间复杂度 \(O(1 ...

  6. NC16681 [NOIP2003]加分二叉树

    题目链接 题目 题目描述 ​ 设一个n个节点的二叉树tree的中序遍历为(l,2,3,-,n),其中数字1,2,3,-,n为节点编号.每个节点都有一个分数(均为正整数),记第j个节点的分数为di,tr ...

  7. Laravel入坑指南(2)——路由、控制器

    接上一节,我们已经把Laravel有Hello World项目跑起来了. 现在各位小友最着急的,应该是想了解,我们怎么在"页面"echo一个自己的Hello World字符串. & ...

  8. Javascript操作对象数组实现增删改查

    1.介绍 最近帮朋友弄一个简单的针对json数组的增删改成页面,正好涉及到了js去操作对象数组实现增删改查功能.我估计很多朋友应该也会遇到这类操作,所以记录一下以便分享. 2.数据准备 这里我就以学生 ...

  9. JSP使用FileUpload上传文件设置setSizeMax后连接被重置

    今天在学习利用JSP+Servlet+FileUpload开发文件上传功能.在设置了上传文件最大限制为10M以后,选择了3M的pdf上传提交后连接被重置了... 我的代码: //设置上传单个文件的大小 ...

  10. 【Android逆向】脱壳项目frida_dump 原理分析

    脱dex核心文件dump_dex.js 核心函数 function dump_dex() { var libart = Process.findModuleByName("libart.so ...