【1】Looper如何和Thread聯(lián)系起來?
答:以主線程為例解釋:
在ActivityThread類中的程序的入口,即main方法,該方法中調(diào)用了:
Looper.prepareMainLooper();
Step:接下來我們解析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(); }
}
官方對(duì)該方法的解釋:
(1)將當(dāng)前線程初始化為looper,將其標(biāo)記為應(yīng)用程序的主looper。
(2)應(yīng)用程序的主looper是由Android環(huán)境創(chuàng)建的,所以你應(yīng)該永遠(yuǎn)不要自己調(diào)用該方法。
Step:接下來我們解析:
prepare(false);
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));}
官方對(duì)該方法的解釋:
(1)該方法讓你有機(jī)會(huì)創(chuàng)建handlers,然后引用這個(gè)looper,然后才開始真正循環(huán)(loop)。
(2)調(diào)用此方法后一定要調(diào)用loop(),通過調(diào)用quit()來結(jié)束它。
Step:接下來我們解析:sThreadLocal.set(new Looper(quitAllowed));
(1)首先我們關(guān)注創(chuàng)建Looper對(duì)象:Looper looper = new Looper(quitAllowed)
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread();}
這里主要是:
(1)創(chuàng)建了MessageQueue的對(duì)象mQueue;
(2)創(chuàng)建了Thread的對(duì)象mThread;
Thread.currentThread()方法可以獲取到當(dāng)前的線程,當(dāng)應(yīng)用程序啟動(dòng)的時(shí)候,系統(tǒng)會(huì)為該應(yīng)用程序創(chuàng)建一個(gè)線程,我們叫它主線程。
(2)其次我們關(guān)注Looper對(duì)象的存儲(chǔ):sThreadLocal.set(looper)
我們看看sThreadLocal是什么東東?
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
官方對(duì)ThreadLocal的解釋:
用來實(shí)現(xiàn)線程本地存儲(chǔ),即每個(gè)線程的變量有自己對(duì)應(yīng)的值。
所有的線程共享相同的ThreadLocal對(duì)象,但是每個(gè)線程訪問它時(shí)都會(huì)看到不同的值,并且有一個(gè)線程更改不會(huì)影響其他線程。
Step:接下來我們解析:set(looper)
public void set(T value) {
Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) {
values = initializeValues(currentThread); }
values.put(this, value);}
官方對(duì)該方法的解釋:
為當(dāng)前線程設(shè)置此變量的值。
這里不對(duì)ThreadLocal類進(jìn)行深入的了解,到這里我們知道使用該類可以實(shí)現(xiàn)存儲(chǔ)Thread和Looper就可以了,類似于(key-value)。
因?yàn)門hread默認(rèn)沒有與它關(guān)聯(lián)的消息循環(huán),(Thread默認(rèn)不能進(jìn)行消息循環(huán))
要?jiǎng)?chuàng)建一個(gè),我們?cè)谶\(yùn)行循環(huán)的線程中調(diào)用prepare(),
然后調(diào)用loop()方法讓他處理消息,
最后調(diào)用quit()方法推出循環(huán)
Looper最重要的方法以及順序:prepare() -> loop() ->quit()
實(shí)際開發(fā)中的使用:參考HandlerThread
【1】extends Thread
【2】Looper.prepare() //將Thread初始化為looper
【3】創(chuàng)建一個(gè)或者多個(gè)Handler,用來處理message
【4】Looper.loop() //分發(fā)message,直到loop被告知quit()
【2】Handler如何和Thread聯(lián)系起來?
主要從分析Handler源碼來解析:
Step:先看默認(rèn)構(gòu)造函數(shù):
public Handler() {
this(null, false);}
官方對(duì)該構(gòu)造函數(shù)的解釋:
默認(rèn)構(gòu)造函數(shù)將該Handler與當(dāng)前線程的Looper關(guān)聯(lián)起來。如果此線程沒有l(wèi)ooper,該Handler將無法接收Message,因此會(huì)拋出異常。
Step:再看看另一個(gè)構(gòu)造函數(shù):
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName()); }
}
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;}
官方對(duì)該構(gòu)造函數(shù)的解釋:
對(duì)于具有指定的回調(diào)接口的當(dāng)前Thread使用Looper,并設(shè)置該Handler是否應(yīng)該是異步的。
Handler默認(rèn)情況下是同步的,除非此構(gòu)造函數(shù)用于創(chuàng)建嚴(yán)格異步的。
mLooper:在Looper中通過prepare()方法創(chuàng)建的,這樣Handler就和Looper聯(lián)系起來了,【同時(shí)和Thread聯(lián)系起來?!?br> mQueue:Handler中的MessageQueue和Looper中的MessageQueue是一致的。
Step:接著看Handler的兩個(gè)主要用途:
【1】調(diào)度message 和 runnable ,在未來的某個(gè)點(diǎn)執(zhí)行。
【2】在與自己不同的線程上執(zhí)行某個(gè)事件。
調(diào)度消息可以通過如下方法完成,大致有兩類型:post 和 send
post方式允許在接收到消息時(shí)將Runnable對(duì)象入隊(duì);
send方式允許在接收到消息時(shí)將Message對(duì)象入隊(duì);
Step:關(guān)于post方式做如下說明:
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);}
官方解釋:
將Runnable添加到MessageQueue.Runnable將在Handler所在的Thread中運(yùn)行。
Step:再看看getPostMessage(r):
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain(); m.callback = r; return m;}
將Runnable用Message包裹起來,以Message的形式發(fā)送出去。
記住:無論是post方式,還是send方式,最終都只調(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; }
return enqueueMessage(queue, msg, uptimeMillis);}
官方解釋:
將Message置入MessageQueue中。(這里時(shí)間先不做解釋了)
Step:再看看enqueueMessage()方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; if (mAsynchronous) {
msg.setAsynchronous(true); }
return queue.enqueueMessage(msg, uptimeMillis);}
重點(diǎn):msg.target = this;這是非常重要的,因?yàn)長(zhǎng)ooper中的loop() 方法會(huì)用到它,即:msg.target.dispatchMessage(msg);
目的是要求發(fā)送Message的Handler和處理Message的Handler是一致的。
Step:接下來重點(diǎn)就是MessageQueue的enqueueMessage()方法,
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target."); }
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use."); }
synchronized (this) {
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; }
msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else {
// Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. 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; }
// We can assume mPtr != 0 because mQuitting is false. if (needWake) {
nativeWake(mPtr); }
}
return true;}
至此,Message已經(jīng)放入MessageQueue中了;
Step:接下來就是從MessageQueue中取Message了,這時(shí)候就需要發(fā)揮Looper的作用了,我們看看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();
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 traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); }
try {
msg.target.dispatchMessage(msg); } finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag); }
}
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(); }
}
這里重點(diǎn)看:msg.target.dispatchMessage(msg)
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg); } else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return; }
}
handleMessage(msg); }
}
這里我們看到它分情況處理了,msg.callback是否為null。
如果msg.callback不為null,說明傳遞過來的是Runnable,然后進(jìn)行handleCallback(msg)
private static void handleCallback(Message message) {
message.callback.run();}
如果msg.callback為null,說明傳遞過來的是Message,然后進(jìn)行handleMessage(msg)
public void handleMessage(Message msg) {
}
我們需要在run()方法和handleMessage()方法中寫自己的實(shí)現(xiàn)。
【3】通過對(duì)【1】和【2】的分析,我發(fā)現(xiàn):
我們主要了解Thread,Looper,Handler它們?nèi)齻€(gè)之間的關(guān)系就可以了,Message和MessageQueue可以把它們當(dāng)作媒介就可以。
出場(chǎng)順序是這樣的:Thread -> Looper -> Handler
Thread創(chuàng)建后,就需要對(duì)Looper進(jìn)行操作,主要是執(zhí)行兩個(gè)方法:prepare(),loop(), 這是準(zhǔn)備工作。
然后就是對(duì)Handler的使用,創(chuàng)建Handler對(duì)象,并將其和Looper聯(lián)系起來,并和Thread聯(lián)系起來,
Handler就可以發(fā)揮它強(qiáng)大的功能。
【4】思考:
UI線程是如何創(chuàng)建的?
應(yīng)用啟動(dòng)時(shí),系統(tǒng)會(huì)為應(yīng)用創(chuàng)建一個(gè)名為主線程的執(zhí)行線程。主線程負(fù)責(zé)將事件分派給相應(yīng)的用戶界面小部件,其中包括繪圖事件。
此外,UI線程也是應(yīng)用與Android UI工具包組件進(jìn)行交互的線程。
UI線程才能處理UI相關(guān)的操作,為什么?
答:Android UI工具包不是線程安全的。因此,單線程模型確保UI不能被不同的線程同時(shí)修改。
Android關(guān)于線程的詳細(xì)說明:https://developer.android.com/guide/components/processes-and-threads.html
【5】Handler的工作原理:
Handler創(chuàng)建時(shí)會(huì)采用當(dāng)前線程的Looper來構(gòu)建內(nèi)部的消息循環(huán)系統(tǒng)。接下來看Handler的運(yùn)行機(jī)制
【6】ThreadLocal:
在不同的線程中訪問的是同一個(gè)ThreadLocal對(duì)象,但是他們通過ThreadLocal獲取到的值是不同的。
why? ->不同線程訪問同一個(gè)ThreadLocal的get()方法,ThreadLocal內(nèi)部會(huì)從各自的線程中取出一個(gè)數(shù)組,