1.為什么要用Handler
在Android中主線程又稱為UI線程,負責(zé)創(chuàng)建和更新UI,其他耗時操作如,訪問數(shù)據(jù)庫,網(wǎng)絡(luò)請求,操作文件IO都需要放在子線程中執(zhí)行。
Android屏幕刷新大概是1秒60幀,每幀間隔16.67毫秒,為了保證刷新不掉幀,所以將耗時操作全部放在子線程中處理。
當(dāng)子線程處理完數(shù)據(jù)之后,為了防止UI處理邏輯的混亂,Android只允許主線程處理UI,這時候需要Handler來充當(dāng)子線程和主線程之間的橋梁。
為什么不加鎖:
1.鎖會讓UI邏輯混亂,無法保證執(zhí)行順序。
2.鎖會降低UI更新效率,會阻塞某些線程的執(zhí)行。
3.Android利用Handler隱示的添加了一把只能在主線程更新的鎖。因此Handler的消息處理機制是線程安全的
ps:在某些情況下可以在子線程更新UI,例如onCreate ,主線程更新完UI后馬上子線程再次更新UI ,或者在子線程更新固定寬高的控件。
2.Handler消息機制的原理
本質(zhì)就是Handler發(fā)送Message到MessageQueue中,Looper遍歷消息分發(fā)給Hanlder處理。
Handler 主要涉及四個類
1.Message:消息
每個消息的攜帶者持有Handler,存放在MessageQueue中,一個線程可以多個實例。
2.Hanlder:消息的發(fā)起者,處理者
發(fā)送Message及處理回調(diào)事件,一個線程可以存在多個實例。
3.Looper:消息的遍歷者
從MessageQueue中遍歷出每個Message進行處理,一個線程一個實例。
4.MessageQueue:消息隊列
存放Handler發(fā)送的消息,提供給Looper遍歷,一個線程一個。
Looper.prepare()
方法中在ThreadLocal中獲取一個Looper。
因此確保每個線程中只有一個Looper。
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
3.Looper的啟動
ActivityThread作為程序的第一個入口,main方法中調(diào)用Looper.prepareMainLooper創(chuàng)建了一個Looper。
并調(diào)用Looper.loop();方法
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
4.Looper的循環(huán)
public static void loop() {
//獲取當(dāng)前線程的Looper對象
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//獲取對應(yīng)的消息隊列
final MessageQueue queue = me.mQueue;
...
//死循環(huán)
for (;;) {
//在消息隊列中獲取消息
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
...
try {
//target 是 Message 中保存處理消息的Handler實例。
msg.target.dispatchMessage(msg);
} catch (Exception exception) {
throw exception;
}
...
msg.recycleUnchecked();
}
}
5.MessageQueue的next();
如果沒有消息可以回去會執(zhí)行nativePollOnce方法陷入沉睡, 等待喚醒。
添加消息到隊列中enqueueMessage方法會進行調(diào)用nativeWake方法喚醒。
6.nativePollOnce 和 nativeWake
目前還無法看懂具體實現(xiàn)。后續(xù)看明白會補上。
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
private native static void nativeWake(long ptr);