Handler简单回顾流程
2019-08-23 本文已影响0人
space0o0
初学android,我们总是被告诫,不能在子线程中更新界面,请用handler。
本文就带大家浅显的理解handler更新界面原理,尽量避免过多源码分析,只贴关键代码帮助记忆。
目录:
- handler的人物交代
- 更新界面的流程
- 拓展和疑问
handler中存在的重要角色
Handler
handle 字面意思:操作,搬运;在程序中理解成搬运工,操作员。
搬运什么呢?搬运消息;什么消息?更新界面的消息。
就这么简单。
MessageQueue
messageQueue中文翻译过来叫:消息队列;
那么handler搬运的消息去哪里了?就放在这个队列中,可以想象成一堆消息排队中,handler搬来的消息都放在了这里,等待被消耗。
Looper
loop中文叫循环;
循环干什么?不停的从消息队列中获取消息。
handler更新流程
- new一个handler(就和new一个女朋友一样简单)
/**
* 1.在UI线程新建handler
*/
fun initHandler() {
mainHandler = @SuppressLint("HandlerLeak")
object : Handler() {
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
//接收不同的消息,做不同的处理
}
}
}
- 通过handler发送消息
/**
* 2.发送消息
*/
fun sendMessage(){
mainHandler.sendMessage(Message())
}
handler有各式各样的send方法,最终都调用sendMessageAtTime方法
//msg:handler搬运的消息
//uptiomeMillis:延时发送 单位 ms
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);//放入消息队列
}
此方法的最终目的就是把消息放到队列中
boolean enqueueMessage(Message msg, long when) {
...其他无关代码...
msg.markInUse();
msg.when = when;//消息的发送时间
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//如果消息队列是空的;
//when==0 代表消息立即处理;
//when<p.when 代表处理的消息最紧急
//满足这些条件,就把消息放在队列最前面
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
//在消息队列根据when的先后顺序,寻找新消息插入的位置
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
//把消息插入队列中
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
通过对消息队列的添加策略,我们得知,消息队列是按照消息执行的先后时间排序的。when越小,排在越前面;
读懂源码的知识点:数据结构中队列的结构和操作。
- Looper开启循环,从消息队列中获取消息
Looper.loop();//开启Looper,该干活了
Message msg = queue.next();//获取新的消息
//MessageQueue的next()方法
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//从队列中拉取消息,nextPollTimeoutMillis=0表示立即拉取
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// now:当前时间 msg.when:消息的通知时间
// 消息的通知时间还不到,计算下次拉取message的时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 消息现在可以执行了,把当前消息从队列中拿出来
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
//返回执行的消息
return msg;
}
} else {
nextPollTimeoutMillis = -1;
}
if (mQuitting) {
dispose();
return null;
}
}
最后调用dispatchMessage通知handler做相应的界面更新
总结一下就是:handler发送消息,MessageQueue保存消息,Looper循环取出消息通知更新界面。
其他handler的细节和知识点在下节分析~