基本概念
Handler
Handler
主要用于異步消息的處理。
與 Looper
溝通亿扁,Push
新消息到 MessageQueue
里,或者接收處理 Looper
從 MessageQueue
里取出的消息药有。
Message
進行消息的封裝窗轩。
Message
類中的關(guān)鍵變量:
- public int what :
用于聲明此Message
的類型。 - public int arg1 :
public int arg2 :
用于傳遞一些整型數(shù)據(jù)介劫。 - public Object obj :
用于傳遞一個對象徽惋。
Message
類中的關(guān)鍵接口方法:
- public static Message obtain() :
優(yōu)先從一個全局的消息池中返回一個新的Message
(復(fù)用消息池找那個的消息)。很多情況下可以避免新生成一個消息的額外開銷座韵。(盡管可以通過New Message()
獲得一個新的消息對象险绘,但是建議優(yōu)先使用obtain()
獲得一個空的Message
,以節(jié)約資源)
Message msg = Message.obtain();
- public Handler getTarget() :
獲取到處理此消息的Handler
對象誉碴。 - public void setData(Bundle data) :
用于傳遞一個Bundle
對象宦棺。對應(yīng)的使用getData()
或peekData()
取出此Bundle
。(注意:如果只是傳遞簡單的int
信息黔帕,應(yīng)優(yōu)先使用arg1
代咸,arg2
)
MessageQueue
被 Looper
持有,用來保存消息(Message
)成黄。
消息隊列是先進先出的呐芥。
消息不會直接添加到 MessageQueue
逻杖,而是通過 Handler
對象和 Looper
。
可以通過 Looper.myQueue()
來獲取當前線程的 MessageQueue
對象思瘟。
Looper
Looper
字面意思就是循環(huán)者荸百。它被設(shè)計用來使一個普通線程變成 Looper
線程,也就是可以循環(huán)工作的線程潮太。
Looper
內(nèi)部維護了一個 MessageQueue
消息隊列管搪。
例如:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare(); //創(chuàng)建Looper對象
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop(); //循環(huán)處理消息隊列
}
}
調(diào)用 quit()
方法結(jié)束 looper
循環(huán)虾攻。
Looper
有以下幾個要點:
- 每個線程有且只能有一個
Looper
對象铡买,它是一個ThreadLocal
。 -
Looper
內(nèi)部有一個消息隊列霎箍,loop()
方法調(diào)用后線程開始不斷從隊列中取出消息執(zhí)行奇钞。 -
Looper
使一個線程變成Looper
線程。
ThreadLocal
線程本地變量漂坏。
每個 Thread
內(nèi)部有一個 ThreadLocal
景埃。
ThreadLocal
不是一個線程而是一個線程的本地化對象。當工作于多線程環(huán)境中的對象采用 ThreadLocal
維護變量時顶别,ThreadLocal
為每個使用該變量的線程分配一個獨立的副本谷徙。每個線程都可以獨立的改變自己的副本,而不影響其他線程的副本驯绎。
ThreadLocal
類中的接口方法:
- public void set(Object value) :
設(shè)置當前線程的線程局部變量的值完慧。 - public Object get() :
返回當前線程的線程局部變量的值。 - public void remove() :
刪除當前線程的局部變量的值剩失。 - protected Object initialValue() :
返回當前線程局部變量的初始值屈尼。
ThreadLocal
是如何做到為每一個線程維護一份獨立的變量副本的呢?
思路很簡單拴孤,在 ThreadLocal
類中有一個 Map
, Map
中的鍵為線程對象脾歧,值為對應(yīng)線程的變量副本。
ThreadLocal
與線程同步機制的比較:
線程同步機制通過對象的鎖機制保證同一時間只有一個線程去訪問變量演熟,該變量時多個線程共享的鞭执。ThreadLocal
則為每一個線程提供了一個變量副本,從而隔離了多個線程訪問數(shù)據(jù)的沖突芒粹,ThreadLocal
提供了線程安全的對象封裝蚕冬,在編寫多線程代碼時,可以把不安全的代碼封裝進 ThreadLocal
是辕。概括的說囤热,對于多線程資源共享的問題,線程同步機制采取了時間換空間的方式获三,訪問串行化旁蔼,對象共享化锨苏;而 ThreadLocal
采取了空間換時間的方式,訪問并行化棺聊,對象獨享化伞租。
使用例子:
public class TestThreadLocal {
private ThreadLocal<Integer> mNum = new ThreadLocal<Integer>() {
public Integer initialValue() {
//設(shè)置默認值為0
return 0;
}
};
public int getNextNum() {
mNum.set(mNum.get() + 1);
return mNum.get();
}
private static class TestThread extends Thread {
private TestThreadLocal mTest;
private TestThread(TestThreadLocal testThreadLocal) {
mTest = testThreadLocal;
}
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("Thread name:" + Thread.currentThread().getName() + ",num:" + mTest.getNextNum());
}
}
}
public static void main(String [] args) {
TestThreadLocal testThreadLocal = new TestThreadLocal();
TestThread thread1 = new TestThread(testThreadLocal);
TestThread thread2 = new TestThread(testThreadLocal);
TestThread thread3 = new TestThread(testThreadLocal);
thread1.start();
thread2.start();
thread3.start();
}
}
打印
Thread name:Thread-0,num:1
Thread name:Thread-1,num:1
Thread name:Thread-2,num:1
Thread name:Thread-1,num:2
Thread name:Thread-0,num:2
Thread name:Thread-2,num:2
Thread name:Thread-0,num:3
Thread name:Thread-2,num:3
Thread name:Thread-1,num:3
Handler 的原理
Handler 原理:
-
Handler
關(guān)聯(lián)線程的Looper
。 -
Handler
發(fā)送消息限佩,通過Looper
的MessageQueue
把消息插入MessageQueue
隊列中葵诈。 -
Looper
不斷循環(huán)份帐,取出MessageQueue
中隊頭的Message
辐啄。 - 調(diào)用
Handler
的dispatchMessage
方法,讓Handler
處理消息霞势。
Handler 和 Looper
Handler
的初始化主要有兩種方式:
- 未指定
Looper
方式:
Handler handler = new Handler();
通過這種方式 new
出來的 Handler
對象晕城,默認使用當前線程的 Looper
泞坦。
源碼如下:
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper(); // 默認將關(guān)聯(lián)當前線程的looper
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue; //關(guān)聯(lián)looper的MQ作為自己的MQ,因此它的消息將發(fā)送到關(guān)聯(lián)looper的MQ上
mCallback = callback;
mAsynchronous = async;
}
注意:
Activity
被創(chuàng)建時就默認創(chuàng)建了 Looper
砖顷,Thread
是沒有默認創(chuàng)建 Looper
的贰锁。
- 指定 Looper 的方式:
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper; //直接設(shè)置為傳入的 Looper 對象
mQueue = looper.mQueue; //關(guān)聯(lián)的也是傳入的 Looper 中的 MQ
mCallback = callback;
mAsynchronous = async;
}
注意:
一個線程可以有對個 Handler
,但是只能有一個 Looper
滤蝠。
Handler 發(fā)送消息
Handler
的使用會有兩種方式豌熄,一種是發(fā)送 Message
、一種是發(fā)送 Runnable
物咳。
- 發(fā)送
Message
:
這種方式有以下接口方法:
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message, long)
sendMessageDelayed(Message, long)
幾個接口調(diào)用如下圖:
可見最后都是調(diào)用的 sendMessageAtTime(Message msg, long uptimeMillis)
接口锣险。
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//重要,這里把當前Handler設(shè)置給msg.target所森,方便后面從MQ中取出消息后囱持,能讓對應(yīng)的Handler處理此消息
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//把消息放入MQ消息隊列
return queue.enqueueMessage(msg, uptimeMillis);
}
- 發(fā)送
Runnable
:
這種方式有以下接口方法:
post(Runnable)
postAtTime(Runnable, long)
postDelayed(Runnable, long)
幾個接口的調(diào)用如下圖:
由上圖可見,最后都會調(diào)用到 sendMessageDelayed(Message msg, long delayMillis)
接口焕济,而 Runnable
者會封裝到 Message
的 callback
變量中纷妆,而 Message
也是放入到消息隊列中的。
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
Looper 中對消息的處理
調(diào)用 Looper
的 loop()
方法后晴弃,Looper
線程就開始了循環(huán)工作掩幢,不斷的從 MessageQueue
中取出隊頭的消息執(zhí)行。
public static void loop() {
//獲取當前線程的Looper
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//獲取當前Looper的MessageQueue
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
//進入死循環(huán)
for (;;) {
//從消息隊列取出隊頭的消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
//關(guān)鍵:msg.target為發(fā)送消息的Handler上鞠,這里把取出來的msg交給Handler處理
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end - start;
if (time > slowDispatchThresholdMs) {
Slog.w(TAG, "Dispatch took " + time + "ms on "
+ Thread.currentThread().getName() + ", h=" +
msg.target + " cb=" + msg.callback + " msg=" + msg.what);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
Handler 中 dispatchMessage 處理消息
上面 Looper
取出消息后际邻,會調(diào)用 Handler
的 dispatchMessage(Message msg)
接口處理消息。
public void dispatchMessage(Message msg) {
//msg.callback 為 Runnable芍阎,即Handler.post(Runnable r) 這種方式調(diào)用的
if (msg.callback != null) {
handleCallback(msg);
} else {
//如果不是post Runnable 的方式世曾,則為sendMessage的方式,直接會調(diào)用handleMessage(msg)處理消息
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
//post runnable 的方式谴咸,handleCallback中直接調(diào)用了Runnable的run方法
message.callback.run();
}
關(guān)于線程問題
由于 Handler
是在它關(guān)聯(lián)的 Looper
線程中處理(dispatchMessage(Message msg))消息的轮听,所以 Looper
所在的線程決定了 Handler
的處理消息的線程骗露。
Activity
的主線程也是一個 Looper
線程,所以在主線程中創(chuàng)建的 Handler
血巍,在子線程中通過 Handler
發(fā)送消息萧锉,最后處理消息時在主(UI)線程中。這也是我們平時為什么能通過 Handler
來解決非主線程中更新 UI
的問題的原因述寡。
如何讓 Handler
關(guān)聯(lián)一個子線程的 Looper
柿隙,使 Handler
在子線程中處理消息?
Android HandlerThread
參考: