1.首先看一段App應用程序啓動的時候的源碼流程分析圖(網上看到的):
從上圖咱們能夠看到:當咱們點擊桌面的應用程序的圖標的時候,首先會經過Binder的IPC通訊機制,啓動ActivityManagerService的startActivity方法,若是發現進程沒有啓動的話,須要經過zygote(受精卵)孵化出一個新的進程,在新的進程中執行ActivityThread的main方法。java
2.咱們在獲取到Looper.getMainLooper()是經過sMainLooper來獲取的,那這個sMainLooper是在哪裏賦值的?
答:首先咱們看到在Looper中有一個方法prepareMainLooper(),該方法會對sMainLooper進行初始化,那麼咱們在何時會調用該方法呢,爲何咱們在主線程中建立handler的時候,不須要手動去調用這個方法,而是直接利用handler的構造方法,不須要對looper進行處理就建立呢?
細看framework的源碼,能夠看到整個 Framework 框架只有兩個地方調用了 prepareMainLooper 方法:c++
第一處是在 SystemServer.java 中的 ServerThread,這個線程是在 Android 啓動過程當中的 main() 方法啓動的:app
public static void main(String[] args) { new SystemServer().run(); } public SystemServer() { mFactoryTestMode = FactoryTest.getMode(); } private void run() { Looper.prepareMainLooper(); Looper.loop(); }
第二處是在 ActivityThread.java 的 main() 方法中:框架
public static void main(String[] args) { ...... Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } AsyncTask.init(); Looper.loop(); }
由於systemServer是超級進程,是在系統啓動的時候,用來初始化各類系統核心服務,是在不一樣於APP的進程中的。(從上面的分析來看的話,Android一個應用開啓對應一個進程,一個進程對應有一個主線程。)根據下圖,咱們能夠明白是在App點擊時候的入口,ActivityThread中對sMainLooper進行賦值的。async
3.爲何在子線程建立handler的時候,須要調用Looper.prepare()方法?
答:如今咱們來看handler的構造方法,對應無Looper形參的構造方法,都會執行下面的構造方法ide
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; }
由於在Looper.prepare()會建立一個looper與當前線程綁定。若是子線程沒有調用prepare方法的話,那個mLooper=null,就會出現上面的報錯了。爲何主線程不須要調用,從1中咱們能夠看到,在app進程建立的時候,會調用Looper.prepareMainLooper(),因此主線程就不用再調用一次了。函數
4.當handler發送一個消息的時候,怎麼將消息插到隊列中?
答:若是發送的是Runnable,不是一個消息,也會在代碼中轉成Message類型,而後最終會執行handler的private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)方法:oop
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
將消息插入到消息隊列當中去。其中的queue是經過looper.mQueue獲取的意味着messageQueue和當前的looper是綁定的,因此一個looper應該是對應一個messageQueue。ui
5.當Message插入到MessageQueue當中,系統是怎麼通知到對應的handler來執行handlerMessage方法呢?
答:若是是在主線程建立Looper的話,默認會調用Looper.loop()方法,會不停地從消息隊列中取出消息,若是消息隊列爲空,則不循環,此時釋放cpu,不會佔用cpu資源,若是有消息的話,那就執行msg.target.dispatchMessage(msg);實際上就是調用handler的dispachMessage方法,會調用handler的handleMessage方法。this
6.在主線程中經過Looper.loop()死循環,爲何不會對主線程形成阻塞呢?
答:由於在該方法中,Looper在建立的過程當中,會建立對應的MessageQueue對象,在構造函數中,會調用JNI的方法nativeInit(),經過這個方法會在c++層建立NativeMessageQueue,同時NativeMessageQueue會建立一個Looper對象,這個Looper對象是經過pipe(管道)設計而成的,若是有消息則喚起讀線程,沒有消息則線程阻塞,釋放cpu資源。回看java層的Looper類的loop方法,Message msg = queue.next(),進入next方法,會執行nativePollOnce(mPtr, nextPollTimeoutMillis),調用JNI層的nativePollOnce方法,會調用NativeMessageQueue的pollOnce(),內部調用了c++層Looper對象的pollOnce(),該方法會調用pollInner(),pollInner()方法就是先經過epoll_wait()進入空閒等待狀態,等待消息隊列的管道上的消息(IO事件)。若是有消息待處理(即管道上有IO寫事件發生,寫事件是EPOLLIN類型),則調用awoken()將消息讀取出來。因此直到有messageQueue中有消息的時候,纔會喚起,若是沒有java層沒有消息的時候,線程是會釋放掉cpu資源的;另外全部的ui操做都經過handler來發消息操做。
結論:
1.咱們經過handler發送一個消息,都是經過looper放置對應的messageQueue當中,而後Looper.loop()會不斷地從messageQueue中獲取消息,有消息的話,就經過msg.target(實際就是handler).handlerMessage();
2.① 每一個Thread只對應一個Looper;② 每一個Looper只對應一個MessageQueue;③ 每一個MessageQueue中有N個Message;④ 每一個Message中最多指定一個Handler來處理事件。一個線程能夠擁有多個handler,可是一個handler只能綁定一個線程。Looper是屬於某一個線程的,一個looper對應一個MessageQueue。判斷這個handleMessage()方法在哪一個線程上執行,就看這個handler的looper對象是在哪一個線程,就在這對應的線程上執行。
若是有什麼不對的,還請指正!