初學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();//獲取新的消息
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的細節和知識點在下節分析~