剛看源碼的時候:「這TM寫的是啥?那寫的又TM是啥?」 研究明白了以後:「奧,原來就這點玩意兒啊,太簡單了。」緩存
Looper的職責很單一,就是單純的從MessageQueue中取出消息分發給消息對應的宿主Handler,所以它的代碼很少(300行左右)。安全
Looper是線程獨立的且每一個線程只能存在一個Looper。bash
Looper會根據本身的存活狀況來建立和退出屬於它本身的MessageQueue。markdown
上面的結論中提到了Looper是線程獨立的且每一個線程只能存在一個Looper。因此構造Looper實例的方法相似於單例模式。隱藏構造方法,對外提供了兩個指定的獲取實例方法prepare()
和prepareMainLooper()
。ide
// 應用主線程(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
關鍵字修飾的,只能被賦值一次。oop
對外公開初始化方法prepareMainLooper()
是爲應用主線程(UI線程)準備的,應用剛被建立就會調用該方法,因此咱們不應再去調用它。性能
開發者能夠經過調用對外公開初始化方法prepare()
對本身的worker線程建立Looper,可是要注意只能初始化一次。ui
調用Looper.prepare()
方法初始化完成後,能夠調用myLooper()
和myQueue()
方法獲得當前線程對應的實例。this
public static @Nullable Looper myLooper() { return sThreadLocal.get(); } public static @NonNull MessageQueue myQueue() { return myLooper().mQueue; } 複製代碼
退出Looperspa
退出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線程準備,理論上開發者在任何狀況下都不該該調用它。