Android的消息機(jī)制主要是指的Handler
的運(yùn)行機(jī)制以及Handler
所附帶的MessageQueue
和Looper
的工作過程,這三者實(shí)際上是一個(gè)整體赶袄。
從開發(fā)的角度來看嚼鹉,Handler是消息機(jī)制的上層接口昆咽,通過它我們可以輕松的將一個(gè)任務(wù)切換到它所在的線程中去執(zhí)行实辑。以消息傳遞為例,在一個(gè)線程創(chuàng)建Handler惜姐,另外一個(gè)線程通過持有該Handler的引用調(diào)用sendMessage實(shí)現(xiàn)消息在線程之間的傳遞。MessageQueue的中文翻譯是消息隊(duì)列椿息,內(nèi)部采用單鏈表的數(shù)據(jù)結(jié)構(gòu)來存儲(chǔ)消息列表歹袁。Looper的中文翻譯是循環(huán)坷衍,這里可以理解為消息循環(huán)。由于MessageQueue只是一個(gè)消息的存儲(chǔ)單元条舔,它不能去處理消息枫耳,而Looper就填補(bǔ)了這個(gè)功能,Looper會(huì)以無限循環(huán)的形式去查找是否有新消息逞刷,如果有的話就處理消息嘉涌,否則就一直等待著。Looper中還有一個(gè)特殊的概念:ThreadLocal
夸浅,當(dāng)我們調(diào)用Looper.prepare()
方法創(chuàng)建Looper時(shí)使用到它仑最。在Handler內(nèi)部就是通過ThreadLocal來獲取每個(gè)線程的Looper的。
Handler的內(nèi)部實(shí)現(xiàn)主要涉及到如下幾個(gè)類: Thread帆喇、MessageQueue和Looper警医。這幾類之間的關(guān)系可以用如下的圖來簡(jiǎn)單說明:
Thread是最基礎(chǔ)的,Looper和MessageQueue都構(gòu)建在Thread之上坯钦,Handler又構(gòu)建在Looper和MessageQueue之上预皇,我們通過Handler間接地與下面這幾個(gè)相對(duì)底層一點(diǎn)的類打交道。
下面我們來先分別介紹ThreadLocal
婉刀,MessageQueue
吟温,Looper
, 和Handler
的工作機(jī)制,再將它們的工作流程進(jìn)行一個(gè)大致的串講突颊。
1. ThreadLocal
1. 概述
ThreadLocal
是一個(gè)線程內(nèi)部的數(shù)據(jù)存儲(chǔ)類鲁豪,通過它可以在指定的線程中存儲(chǔ)數(shù)據(jù),數(shù)據(jù)存儲(chǔ)以后律秃,只有在指定線程中可以獲取到存儲(chǔ)的數(shù)據(jù)爬橡,對(duì)于其他線程來說則無法獲取到數(shù)據(jù)。
上面的話可以理解為棒动,ThreadLocal
是一個(gè)全局變量糙申,用來存儲(chǔ)對(duì)應(yīng)Thread
的本地變量。當(dāng)使用ThreadLocal維護(hù)變量時(shí)船惨,ThreadLocal為每個(gè)使用該變量的線程提供獨(dú)立的變量副本柜裸,所以每一個(gè)線程都可以獨(dú)立地改變自己的副本,而不會(huì)影響其它線程所對(duì)應(yīng)的副本粱锐。 舉一個(gè)簡(jiǎn)單的例子:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
//定義ThreadLocal對(duì)象
private ThreadLocal<Boolean> mBooleanThreadLocal = new ThreadLocal<>();
?
?
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
?
mBooleanThreadLocal.set(true);
Log.d(TAG, "[Thread#main]mBooleanThreadLocal=" + mBooleanThreadLocal.get());
?
new Thread("Thread#1") {
@Override
public void run() {
mBooleanThreadLocal.set(false);
Log.d(TAG, "[Thread#1]mBooleanThreadLocal=" + mBooleanThreadLocal.get());
}
}.start();
?
new Thread("Thread#2") {
@Override
public void run() {
Log.d(TAG, "[Thread#2]mBooleanThreadLocal=" + mBooleanThreadLocal.get());
}
}.start();
}
}
在上面的代碼中粘室,在主線程中設(shè)置mBooleanThreadLocal
的值為true
,在子線程1中設(shè)置mBooleanThreadLocal
值為false
卜范,子線程2中不設(shè)置mBooleanThreadLocal
的值衔统。然后在3個(gè)線程中分別通過get
方法獲取值并打印出來。根據(jù)前面的描述,期望的打印結(jié)果應(yīng)該是:主線程為true
锦爵,子線程1為false
舱殿,子線程2位null
,因?yàn)樽泳€程2中沒有設(shè)置值险掀。實(shí)際打印結(jié)果如下:
可以看到沪袭,盡管在三個(gè)線程中訪問的為同一個(gè)對(duì)象,但是ThreadLocal為他們各自維護(hù)了一個(gè)該對(duì)象的副本樟氢,所以訪問到的結(jié)果不同冈绊。
2. 實(shí)現(xiàn)原理
ThreadLocal是一個(gè)泛型類,public class ThreadLocal<T>
埠啃,要理解它的工作原理死宣,可以從get
和set
方法入手,先來看set
方法:
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
可見碴开,首先通過getMap
方法來獲取當(dāng)前線程中的ThreadLocal
數(shù)據(jù)毅该,如果返回的ThreadLocalMap
對(duì)象不為空,則調(diào)用set
方法添加數(shù)據(jù)潦牛,否則就創(chuàng)建一個(gè)新對(duì)象并把值添加進(jìn)去眶掌。ThreadLocalMap
中定義了一個(gè)private Entry[] table;
來存儲(chǔ)數(shù)據(jù),我們可以通過set
方法將數(shù)據(jù)添加到table
數(shù)組中巴碗。
再來看看get
方法:
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
可以發(fā)現(xiàn)朴爬,ThreadLocal
的get
方法同樣是先取出當(dāng)前線程的ThreadLocalMap
對(duì)象,如果這個(gè)對(duì)象為null
就返回初始值橡淆,這個(gè)初始值由ThreadLocal
的initialValue()
方法來確定寝殴,默認(rèn)情況下為null
,默認(rèn)實(shí)現(xiàn)如下所示:
protected T initialValue() {
return null;
}
如果ThreadLocalMap
對(duì)象不為null
明垢,那么就取出table
數(shù)組并找到相應(yīng)的值返回回去。
從ThreadLocal
的set
和get
方法可以看出市咽,它們所操作的對(duì)象都是當(dāng)前線程的ThreadLocalMap
對(duì)象的table
數(shù)組痊银,因此在不同線程中訪問同一個(gè)ThreadLocal
的set
和get
方法,它們對(duì)ThreadLocal
所做的讀/寫操作僅限于各線程的內(nèi)部施绎,所以ThreadLocal
可以在多個(gè)線程中互不干擾地存儲(chǔ)和修改數(shù)據(jù)溯革。
3. 與Android消息機(jī)制的聯(lián)系
當(dāng)我們?cè)谝粋€(gè)線程中創(chuàng)建一個(gè)Handler,調(diào)用Looper.prepare()時(shí)通過ThreadLocal保存當(dāng)前線程下的Looper對(duì)象谷醉,而所有線程的Looper都由一個(gè)ThreadLocal來維護(hù)致稀,也就是在所有線程中創(chuàng)建的Looper都存放在了同一個(gè)ThreadLocal中。而Handler又與Looper協(xié)同工作俱尼,大致關(guān)系如下圖:
2. MessageQueue
最基礎(chǔ)最底層的是Thread抖单,每個(gè)線程內(nèi)部都維護(hù)了一個(gè)消息隊(duì)列——MessageQueue。消息隊(duì)列MessageQueue,顧名思義矛绘,就是存放消息的隊(duì)列(好像是廢話…)耍休。那隊(duì)列中存儲(chǔ)的消息是什么呢?假設(shè)我們?cè)赨I界面上單擊了某個(gè)按鈕货矮,而此時(shí)程序又恰好收到了某個(gè)廣播事件羊精,那我們?nèi)绾翁幚磉@兩件事呢? 因?yàn)橐粋€(gè)線程在某一時(shí)刻只能處理一件事情囚玫,不能同時(shí)處理多件事情喧锦,所以我們不能同時(shí)處理按鈕的單擊事件和廣播事件,我們只能挨個(gè)對(duì)其進(jìn)行處理抓督,只要挨個(gè)處理就要有處理的先后順序燃少。 為此Android把UI界面上單擊按鈕的事件封裝成了一個(gè)Message,將其放入到MessageQueue里面去本昏,即將單擊按鈕事件的Message入棧到消息隊(duì)列中供汛,然后再將廣播事件的封裝成以Message,也將其入棧到消息隊(duì)列中涌穆。也就是說一個(gè)Message對(duì)象表示的是線程需要處理的一件事情怔昨,消息隊(duì)列就是一堆需要處理的Message的池。線程Thread會(huì)依次取出消息隊(duì)列中的消息宿稀,依次對(duì)其進(jìn)行處理趁舀。MessageQueue中有兩個(gè)比較重要的方法,一個(gè)是enqueueMessage方法祝沸,一個(gè)是next方法矮烹。enqueueMessage方法用于將一個(gè)Message放入到消息隊(duì)列MessageQueue中,next方法是從消息隊(duì)列MessageQueue中阻塞式地取出一個(gè)Message罩锐。
enqueueMessage
主要操作其實(shí)就是單鏈表的插入操作奉狈,這里就不再過多解釋了。而next
方法是一個(gè)無限循環(huán)的方法涩惑,如果消息隊(duì)列中沒有消息仁期,那么next
方法會(huì)一直阻塞在這里。當(dāng)有新消息到來時(shí)竭恬,next
方法會(huì)返回這條消息并將其從單鏈表中移除跛蛋。
3. Looper
1. Looper的創(chuàng)建(使用ThreadLocal存儲(chǔ))
Looper
在Android
的消息機(jī)制中扮演著消息循環(huán)的角色,具體來說就是它會(huì)不停地從MessageQueue
中查看是否有新消息痊硕,如果有新消息就會(huì)立刻處理赊级,否則就一直阻塞在哪里。首先來看一下它的構(gòu)造方法岔绸,在構(gòu)造方法中它會(huì)創(chuàng)建一個(gè)MessageQueue
即消息隊(duì)列理逊,然后將當(dāng)前線程的對(duì)象保存起來橡伞,如下所示:
@UnsupportedAppUsage
final MessageQueue mQueue;
final Thread mThread;
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
創(chuàng)建looper
時(shí)調(diào)用looper.prepare
,具體操作:
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));
}
sThreadLocal即為存儲(chǔ)各線程Looper的ThreadLocal對(duì)象挡鞍,調(diào)用其get方法判斷該線程是否已經(jīng)持有了Looper骑歹,如果已經(jīng)持有則拋出異常,這是為了保證對(duì)于每個(gè)線程Looper的唯一性墨微。如果沒有Looper道媚,則為該線程創(chuàng)建一個(gè)Looper。
2. Looper的循環(huán)
而Looper
最重要的一個(gè)方法是loop
方法翘县,只有調(diào)用了loop
后最域,消息循環(huán)系統(tǒng)才會(huì)真正地起作用,它的實(shí)現(xiàn)如下:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
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();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
boolean slowDeliveryDetected = false;
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);
}
// Make sure the observer won't change while processing a transaction.
final Observer observer = sObserver;
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
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();
}
}
選取其中比較關(guān)鍵的部分做一下講解:
- final MessageQueue queue = me.mQueue;
變量me是通過靜態(tài)方法myLooper()獲得的當(dāng)前線程所綁定的Looper锈麸,me.mQueue是當(dāng)前線程所關(guān)聯(lián)的消息
隊(duì)列镀脂。
- for (;;)
我們發(fā)現(xiàn)for循環(huán)沒有設(shè)置循環(huán)終止的條件,所以這個(gè)for循環(huán)是個(gè)死循環(huán)忘伞。
- Message msg = queue.next(); // might block
我們通過消息隊(duì)列MessageQueue的next方法從消息隊(duì)列中取出一條消息薄翅,如果此時(shí)消息隊(duì)列中有Message,
那么next方法會(huì)立即返回該Message氓奈,如果此時(shí)消息隊(duì)列中沒有Message翘魄,那么next方法就會(huì)阻塞式地等待獲
取Message。
- msg.target.dispatchMessage(msg);
msg的target屬性是Handler舀奶,該代碼的意思是讓Message所關(guān)聯(lián)的Handler通過dispatchMessage方法讓
Handler處理該Message暑竟,關(guān)于Handler的dispatchMessage方法將會(huì)在下面詳細(xì)介紹。
loop
方法是一個(gè)死循環(huán)育勺,唯一跳出循環(huán)的方式是MessageQueue
的next
方法返回了null
但荤。
當(dāng)Looper
的quit方法被調(diào)用時(shí),Looper
就會(huì)調(diào)用MessageQueue
的quit方法或quitSafely方法來通知消息隊(duì)列退出涧至,當(dāng)消息隊(duì)列被標(biāo)記為退出狀態(tài)時(shí)腹躁,它的next
方法就會(huì)返回null
。如果MessageQueue
的next
方法返回了新消息南蓬,Looper
就會(huì)處理這條消息: msg.target.dispatchMessage(msg)
纺非,這里的msg.target
是發(fā)送這條消息的Handler
對(duì)象,這樣Handler
發(fā)送的消息最終又交給它的dispatchMessage
方法來處理了蓖康。但是這里不同的是,Handler
的dispatchMessage
方法是在創(chuàng)建Handler
時(shí)所使用的Looper
中執(zhí)行的垒手,這樣就成功的將代碼邏輯切換到指定的線程中去執(zhí)行了蒜焊。
4. Handler
1. 消息發(fā)送
Handler
的工作主要包含消息的發(fā)送和接收過程。消息的發(fā)送最終是通過send
的一系列方法來實(shí)現(xiàn)的科贬。發(fā)送一條消息的典型過程如下所示:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在enqueueMessage中有兩件事需要注意:
- msg.target = this
該代碼將Message的target綁定為當(dāng)前的Handler - queue.enqueueMessage
變量queue表示的是Handler所綁定的消息隊(duì)列MessageQueue泳梆,通過調(diào)用queue.enqueueMessage(msg, uptimeMillis)我們將Message放入到消息隊(duì)列中鳖悠。
其他的一些調(diào)用最終也都?xì)w結(jié)到上面這個(gè)流程中:
2. 消息處理
通過上文可以發(fā)現(xiàn),Handler
發(fā)送消息的過程僅僅是向消息隊(duì)列插入了一條消息优妙,MessageQueue
的next
方法就會(huì)返回這條消息給Looper
乘综,Looper
收到消息后就開始處理了,最終消息由Looper
交由Handler
處理套硼,即Handler
的dispatchMessage
方法會(huì)被調(diào)用卡辰,這時(shí)Handler就進(jìn)入了處理消息的階段。dispatchMessage
的實(shí)現(xiàn)如下所示:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
可以看到邪意,如果我們?cè)O(shè)置了callback(Runnable對(duì)象)的話九妈,則會(huì)直接調(diào)用handleCallback方法
private static void handleCallback(Message message) {
message.callback.run();
}
即,如果我們?cè)诔跏蓟疕andler的時(shí)候設(shè)置了callback(Runnable)對(duì)象雾鬼,則直接調(diào)用run方法萌朱。比如我們經(jīng)常寫的runOnUiThread方法:
runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
而如果msg.callback為空的話,會(huì)直接調(diào)用我們的mCallback.handleMessage(msg)策菜,即handler的handlerMessage方法晶疼。handlerMessage方法的執(zhí)行也會(huì)在創(chuàng)建handler的線程中。
綜上又憨,我們可以看到Handler提供了三種途徑處理Message翠霍,而且處理有前后優(yōu)先級(jí)之分:首先嘗試讓postXXX中傳遞的Runnable執(zhí)行,其次嘗試讓Handler構(gòu)造函數(shù)中傳入的Callback的handleMessage方法處理竟块,最后才是讓Handler自身的handleMessage方法處理Message壶运。
3. post與send的比較
上述例子都是使用send方法,這里加一個(gè)post的例子幫助理解handler對(duì)于不同類型信息的處理
首先浪秘,調(diào)用post方法時(shí)依然調(diào)用了sendMessageDelayed蒋情,但是值得注意的是這里的參數(shù)有所不同,使用了getPostMessage(r)作為參數(shù)耸携。
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);//getPostMessage方法是兩種發(fā)送消息的不同之處
}
那么我們來看一看??這個(gè)getPostMessage方法:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
可以看到棵癣,依然是把Runnable對(duì)象封裝成了一個(gè)Message進(jìn)行發(fā)送,不過這里設(shè)置了m.callback = r 夺衍,這也呼應(yīng)了上文中提到的狈谊,如果我們?cè)诔跏蓟疕andler的時(shí)候設(shè)置了callback(Runnable)對(duì)象,則直接調(diào)用run方法沟沙。
4. Callback
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
Handler.Callback是用來處理Message的一種手段河劝,如果沒有傳遞該參數(shù),那么就應(yīng)該重寫Handler的handleMessage方法矛紫,也就是說為了使得Handler能夠處理Message赎瞎,我們有兩種辦法:
- 向Hanlder的構(gòu)造函數(shù)傳入一個(gè)Handler.Callback對(duì)象,并實(shí)現(xiàn)Handler.Callback的handleMessage方法
- 無需向Hanlder的構(gòu)造函數(shù)傳入Handler.Callback對(duì)象颊咬,但是需要重寫Handler本身的handleMessage方法
也就是說無論哪種方式务甥,我們都得通過某種方式實(shí)現(xiàn)handleMessage方法牡辽,這點(diǎn)與Java中對(duì)Thread的設(shè)計(jì)有異曲同工之處。
在Java中敞临,如果我們想使用多線程态辛,有兩種辦法:
向Thread的構(gòu)造函數(shù)傳入一個(gè)Runnable對(duì)象,并實(shí)現(xiàn)Runnable的run方法
無需向Thread的構(gòu)造函數(shù)傳入Runnable對(duì)象挺尿,但是要重寫Thread本身的run方法
5. 總結(jié)
在使用handler的時(shí)候奏黑,在handler所創(chuàng)建的線程需要維護(hù)一個(gè)唯一的Looper對(duì)象, 每個(gè)線程對(duì)應(yīng)一個(gè)Looper票髓,每個(gè)線程的Looper通過ThreadLocal來保證
Looper對(duì)象的內(nèi)部又維護(hù)有唯一的一個(gè)MessageQueue攀涵,所以一個(gè)線程可以有多個(gè)handler,
但是只能有一個(gè)Looper和一個(gè)MessageQueue洽沟。Message在MessageQueue不是通過一個(gè)列表來存儲(chǔ)的以故,而是將傳入的Message存入到了上一個(gè)
Message的next中,在取出的時(shí)候通過頂部的Message就能按放入的順序依次取出Message裆操。Looper對(duì)象通過loop()方法開啟了一個(gè)死循環(huán)怒详,不斷地從looper內(nèi)的MessageQueue中取出Message,
然后通過handler將消息分發(fā)傳回handler所在的線程踪区。-
handler收到消息以后昆烁,通過handleMessage進(jìn)行消息處理
6. 補(bǔ)充:資源管理與內(nèi)存泄漏
1. Handler的內(nèi)存泄漏問題
Handler使用是用來進(jìn)行線程間通信的,所以新開啟的線程會(huì)持有Handler引用缎岗,如果在Activity等中創(chuàng)建Handler静尼,并且是非靜態(tài)內(nèi)部類的形式,就有可能造成內(nèi)存泄漏传泊。
首先鼠渺,非靜態(tài)內(nèi)部類是會(huì)隱式持有外部類的引用,所以當(dāng)其他線程持有了該Handler眷细,線程沒有被銷毀拦盹,則意味著Activity會(huì)一直被Handler持有引用而無法導(dǎo)致回收。
同時(shí)溪椎,MessageQueue中如果存在未處理完的Message普舆,Message的target也是對(duì)Activity等的持有引用,也會(huì)
造成內(nèi)存泄漏校读。
解決的辦法:
-
使用靜態(tài)內(nèi)部類+弱引用的方式:
靜態(tài)內(nèi)部類不會(huì)持有外部類的的引用沼侣,當(dāng)需要引用外部類相關(guān)操作時(shí),可以通過弱引用還獲取到外部類相關(guān)操作歉秫,弱引用不會(huì)造成對(duì)象該回收回收不掉的問題蛾洛。
private Handler sHandler = new TestHandler(this); static class TestHandler extends Handler { private WeakReference<Activity> mActivity; TestHandler(Activity activity) { mActivity = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); Activity activity = mActivity.get(); if (activity != null) { //TODO: } } }
-
在外部類對(duì)象被銷毀時(shí),將MessageQueue中的消息清空端考。例如雅潭,在Activity的onDestroy時(shí)將消息清空。
@Override protected void onDestroy() { handler.removeCallbacksAndMessages(null); super.onDestroy(); }
2. 創(chuàng)建Message時(shí)的資源管理
使用Handler.obtainMessage()來獲取Message對(duì)象的却特,和直接new一個(gè)Message有什么差別呢扶供?
Message message = handler.obtainMessage();
Message message = new Message();
看一看obtain的代碼:
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
在Message中有一個(gè)static Message變量sPool,這個(gè)變量是用于緩存Message對(duì)象的裂明,在obtain中可以看到當(dāng)需要一個(gè)Message對(duì)象時(shí)椿浓,如果sPool不為空則會(huì)返回當(dāng)前sPool(Message),而將sPool指向了之前sPool的next對(duì)象闽晦,(之前講MessageQueue時(shí)講過Message的存儲(chǔ)是以鏈?zhǔn)降男问酱鎯?chǔ)的扳碍,通過Message的next指向下一個(gè)Message,這里就是返回了sPool當(dāng)前這個(gè)Message仙蛉,然后sPool重新指向了其下一個(gè)Message)笋敞,然后將返回的Message的next指向置為空(斷開鏈表),sPoolSize記錄了當(dāng)前緩存的Message的數(shù)量荠瘪,如果sPool為空夯巷,則沒有緩存的Message,則需要?jiǎng)?chuàng)建一個(gè)新的Message(new Message)哀墓。
那么趁餐,緩存中的sPool是哪里來的呢
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
recycle()是回收Message的方法,在Message處理完或者清空Message等時(shí)會(huì)調(diào)用。recycleUnchecked()方法中可以看到篮绰,將what后雷、arg1、arg2吠各、object等都重置了值臀突,如果當(dāng)前sPool(Message緩存池)的大小小于允許緩存的Message最大數(shù)量時(shí),將要回收的Message的next指向sPool走孽,將sPool指向了回收的Message對(duì)象(即將Message放到了sPool緩存池的頭部)
7. 參考文章
這些是我在準(zhǔn)備和學(xué)習(xí)過程中參考的一些博客惧辈,大家有興趣可以自己再康康??
Android消息機(jī)制: http://www.reibang.com/p/7653adc038c6
Android消息機(jī)制: https://blog.csdn.net/wsq_tomato/article/details/80301851?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
post()和postDelay()方法精煉詳解: https://blog.csdn.net/weixin_41101173/article/details/79701832
深入源碼解析Android中的Handler,Message,MessageQueue,Looper:https://blog.csdn.net/iispring/article/details/47180325