剛看源碼的時候:「這TM寫的是啥?那寫的又TM是啥?」 研究明白了以後:「奧,原來就這點玩意兒啊,太簡單了。」緩存
Looper的職責很單一,就是單純的從MessageQueue中取出消息分發給消息對應的宿主Handler,所以它的代碼很少(300行左右)。安全
Looper是線程獨立的且每一個線程只能存在一個Looper。bash
Looper會根據本身的存活狀況來建立和退出屬於它本身的MessageQueue。ide
上面的結論中提到了Looper是線程獨立的且每一個線程只能存在一個Looper。因此構造Looper實例的方法相似於單例模式。隱藏構造方法,對外提供了兩個指定的獲取實例方法prepare()
和prepareMainLooper()
。oop
// 應用主線程(UI線程)Looper實例
private static Looper sMainLooper;
// Worker線程Looper實例,用ThreadLocal保存的對象都是線程獨立的
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
// 與當前Looper對應的消息隊列
final MessageQueue mQueue;
// 當前Looper因此的線程
final Thread mThread;
/**
* 對外公開初始化方法
*
* 在普通線程中初始化Looper調用此方法
*/
public static void prepare() {
// 初始化一個能夠退出的Looper
prepare(true);
}
/**
* 對外公開初始化方法
*
* 在應用主線程(UI線程)中初始化Looper調用此方法
*/
public static void prepareMainLooper() {
// 由於是主線程,初始化一個不容許退出的Looper
prepare(false);
synchronized (Looper.class) {
// 若是sMainLooper不等於空說明已經建立過主線程Looper了,不該該重複建立
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
/**
* 內部私有初始化方法
* @param quitAllowed 是否容許退出Looper
*/
private static void prepare(boolean quitAllowed) {
// 每一個線程只能有一個Looper
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 保存實例
sThreadLocal.set(new Looper(quitAllowed));
}
/**
* 私有構造方法
* @param quitAllowed 是否容許退出Looper
*/
private Looper(boolean quitAllowed) {
// 初始化MessageQueue
mQueue = new MessageQueue(quitAllowed);
// 獲得當前線程實例
mThread = Thread.currentThread();
}
複製代碼
真正建立Looper實例的構造方法中其實很簡單,就是建立了對應的MessageQueue實例,而後獲得當前線程,值得注意的是MessageQueue和線程實例都是被final
關鍵字修飾的,只能被賦值一次。性能
對外公開初始化方法prepareMainLooper()
是爲應用主線程(UI線程)準備的,應用剛被建立就會調用該方法,因此咱們不應再去調用它。ui
開發者能夠經過調用對外公開初始化方法prepare()
對本身的worker線程建立Looper,可是要注意只能初始化一次。this
調用Looper.prepare()
方法初始化完成後,能夠調用myLooper()
和myQueue()
方法獲得當前線程對應的實例。spa
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
public static @NonNull MessageQueue myQueue() {
return myLooper().mQueue;
}
複製代碼
退出Looper線程
退出Looper有安全與不安全兩種退出方法,其實對應的就是MessageQueue的安全與不安全方法:
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
複製代碼
什麼安全退出,什麼是不安全退出,在MessageQueue源碼中分析過。
調用Looper.prepare()
方法初始化完成Looper後就可讓Looper去工做了,只須要調用Looper.loop()
方法便可。
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對應的消息隊列
final MessageQueue queue = me.mQueue;
// 獲得當前線程的惟一標識(uid+pid),做用是下面每次循環都判斷一下線程有沒有被切換
// 不知道爲何要調用兩次該方法
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// 進入死循環不斷取出消息
for (;;) {
// 從隊列中取出一個消息,這可能會阻塞線程
Message msg = queue.next();
// 若是消息是空的,說明隊列已經退出了,直接結束循環,結束方法
if (msg == null) {
return;
}
// 打印日誌
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 {
//嘗試將消息分發給宿主(Handler)
//dispatchMessage爲宿主Handler的接收消息方法
msg.target.dispatchMessage(msg);
} finally {
// 性能分析相關的東西
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//打印日誌
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
//獲得當前線程的惟一標識
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();
}
}
複製代碼
Looper的功能很簡單,核心方法Looper.loop()
就是不斷的從消息隊列中取出消息分發給對應的宿主Handler,它與對應MessageQueue息息相關,一塊兒建立,一塊兒退出。
Looper更想強調的是線程的獨立性與惟一性,利用ThreadLocal
保證每一個線程只有一個Looper實例的存在。利用靜態構造實例方法保證不能重複建立Looper。
Looper.prepareMainLooper()
是比較特殊的方法,它是給UI線程準備,理論上開發者在任何狀況下都不該該調用它。