前言
- 在Android中經(jīng)常使用多線程開發(fā)偷拔,Handler則是基礎(chǔ),那么本文主要以流程+源碼的形式來記錄這種機(jī)制
Why?
為什么需要Handler?
我們需要切換線程處理UI垃帅,是因?yàn)閂iewRootImpl#checkThread()中會(huì)判斷創(chuàng)建View的線程是否與修改View的線程為同一個(gè)
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
How?
Android中是如何設(shè)計(jì)的?
使用不同的類協(xié)作完成
名稱 | 作用 |
---|---|
Message | 存儲(chǔ)信息數(shù)據(jù) |
MessageQueue | 存儲(chǔ)Message |
Looper | 循環(huán)/處理消息 |
Handler | 發(fā)送和接受消息 |
流程圖
結(jié)合源碼
提前的預(yù)備:線程中需要提前創(chuàng)建Looper和MessageQueue
- 創(chuàng)建Looper的兩種方式
Looper.prepare();
Looper.prepareMainLooper();
Looper的構(gòu)造方法是私有的只允許這兩種方式創(chuàng)建
//普通的創(chuàng)建 Looper.prepare();
private static void prepare(boolean quitAllowed) {
//ThreadLocal封裝了一個(gè)ThreadLocalMap(key:ThreadLocal<?>,value:Obj)保存線程和Looper的信息
//如果獲取到了就說明這個(gè)線程有對應(yīng)的Looper對象
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//將Looper和當(dāng)前線程信息保存
sThreadLocal.set(new Looper(quitAllowed));
}
//主線程創(chuàng)建 Looper.prepareMainLooper();
//在ActivityThread的main方法中
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
以上可看出 每個(gè)線程都只能對應(yīng)一個(gè)Looper
- 創(chuàng)建MessageQueue
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
創(chuàng)建Looper的時(shí)候就順帶創(chuàng)建了MessageQueue,所以一個(gè)線程也只有一個(gè)MessageQueue
開始循環(huán)等待消息到來
Looper.loop();
public static void loop() {
//這里就是 final Looper me = sThreadLocal.get();
//找到此線程對應(yīng)的Looper
final Looper me = myLooper();
//必須創(chuàng)建Looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//獲取MessageQueue對象
final MessageQueue queue = me.mQueue;
//for循環(huán)讀取消息
for (;;) {
//MessageQueue中有消息就返回 沒消息就阻塞
Message msg = queue.next();
if (msg == null) {
return;
}
//target是一個(gè)handler對象 此處分發(fā)了消息
msg.target.dispatchMessage(msg);
//回收
msg.recycleUnchecked();
}
}
收到消息后的處理
將消息對接給Handler 即dispatchMessage(Message msg);
public void dispatchMessage(Message msg) {
// callback不為空,則使用了post(Runnable r)發(fā)送消息
// 執(zhí)行handleCallback(msg),即回調(diào)Runnable對象里復(fù)寫的run()
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//若msg.callback屬性為空恨胚,則使用了sendMessage發(fā)送消息
// 執(zhí)行handleMessage(msg),即回調(diào)復(fù)寫的handleMessage(msg)
handleMessage(msg);
}
}
Handler的創(chuàng)建
public Handler(Callback callback, boolean async) {
...
//綁定對應(yīng)的looper炎咖、MessageQueue赃泡、callback
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Handler準(zhǔn)備發(fā)送Message
發(fā)送Message有兩種方式 也就是dispatchMessage(Message msg)中用來區(qū)分的
1.handler.post(Runnable runnable);
2.handler.sendMessage(Message msg);
- handler.post(Runnable runnable);
public final boolean post(Runnable r) {
//先是將runnable封裝到Message中再send
//這里后續(xù) 在第二種方式里
return sendMessageDelayed(getPostMessage(r), 0);
}
//這里也是把runnable當(dāng)做一個(gè)屬性賦值給Message
//之后在dispatchMessage(Message msg)時(shí) 根據(jù)callback有無判斷那種方式
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
- handler.sendMessage(msg);
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
//第一種也是調(diào)用這個(gè)方法
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//最終調(diào)用這個(gè):
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;
}
//將消息放入MessageQueue中
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//此處將Handler賦值給target
msg.target = this;
if (mAsynchronous) {
//設(shè)置消息異步,異步不會(huì)受同步障礙(某些操作會(huì)在消息隊(duì)列引入同步障礙乘盼,防止后續(xù)消息傳遞確保無效請求在恢復(fù)前得到處理)的約束
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
將消息添加到MessageQueue中
MessageQueue.enqueueMessage(Message msg, long when);
boolean enqueueMessage(Message msg, long when) {
//檢查消息是否有對應(yīng)的Handler
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//檢查消息是否已經(jīng)被使用過
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
//是否調(diào)用過MessageQueue.quit(boolean safe)方法退出
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
//標(biāo)記已經(jīng)使用了
msg.markInUse();
//消息目標(biāo)發(fā)送時(shí)間 越小優(yōu)先級越高
msg.when = when;
//獲取頭部消息
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//插入新的頭部升熊,如果阻塞就喚醒消息隊(duì)列
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//將消息插入隊(duì)列中
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// 消息有同步屏障或者隊(duì)列中有最早的異步消息 否則不需要喚醒
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
這樣就把消息加入到消息隊(duì)列中了,在這之后就回到前面Looper那邊分發(fā)給Handler處理
看到這里再回頭看看流程圖和下面的總結(jié)會(huì)更好理解一些
注意要點(diǎn)
1.關(guān)于內(nèi)存泄漏
- 采用靜態(tài)內(nèi)部類:static handler = xxx
- Activity結(jié)束時(shí),調(diào)用handler.removeCallback()蹦肴、然后handler設(shè)置為null
- 如果使用到Context等引用僚碎,要使用弱引用
2.MessageQueue其實(shí)是單鏈表數(shù)據(jù)結(jié)構(gòu)主要是為了提高插入消息、刪除消息的效率
3.一個(gè)線程最多只能有一個(gè)Looper
總結(jié)
最后
之前看了很多次關(guān)于這方面的知識(shí)阴幌,但總是感覺斷斷續(xù)續(xù)勺阐,模模糊糊,所以這次做一個(gè)小小的總結(jié)矛双,留給以后回顧渊抽,如果文中如果有什么紕漏歡迎討論與指出。