閱讀時間:8 分鐘編程
坐穩了沒?要開車了哦安全
寫在前邊服務器
今天分享的內容是深刻剖析 Android 的消息機制,有些人問了,一些功能會用不就好了嗎?爲何還要分析底層源碼呢?今天小鹿告訴你的是不少開源的項目都已經不須要咱們造輪子了,重複造輪子是多麼愚蠢的一件事。可是,Android 的底層源碼和一些功能的實現讓咱們學習到底層的模式和邏輯實現。數據結構
學編程什麼最重要,固然是邏輯思惟了,即便你什麼功能都能實現,邏輯思惟能力差照樣啥都幹不了。你的思惟邏輯能力差,在技術路線上已經決定了你的高度。多線程
Android 的消息機制併發
Android 的消息機制主要是指 Handlerr 的運行須要底層的 MessageQueue 和 Looper 的支撐。ide
(1)MessageQueue 的中文翻譯是消息隊列。以隊列的形式對外提供插入和刪除工做。雖然叫作消息隊列,可是內部存儲結構並非真正的隊列,而是以單鏈表的數據結構來存儲消息列表。oop
(2)Looper 的中文翻譯爲循環,咱們叫它消息循環。因爲 MessageQueue 只是一個存儲單元,不會去處理消息。而 Looper 確彌補了這個功能,Looper 會以無限無限循環的形式去查找是否有新的消息,有的話就去處理消息,不然就一直等待。源碼分析
學習思惟導圖:post
之後文章中的思惟導圖是小鹿給你們精心整理的,這樣對每篇分享的文章都有一個清晰地結構,有利於複習和整理。
1、Android 消息機制概述
Android 消息機制主要是指 Handler 的運行機制以及 Handler 所附帶的 MessageQueue 和 Looper 的工做過程。Handler 的主要做用就是將一個任務切換到某個指定的線程中去執行。
概述
(1)思考:爲何 Android 要提供這個功能呢?
答:由於 Android 規定訪問 UI 線程只能在主線程中進行的,若是在子線程中訪問 UI ,那麼程序就會拋出異常。
(2)源碼:通過查看看源碼的 checkThread()方法對更新 UI 是否在主線程中更新,進行拋出異常信息提示開發者(相信在開發中都遇到過這個種狀況)。
(3)過程:因爲以上限制,這就要求開發者必須在主線程中更新 UI,可是 Android 又建議不要在主線程中進行過於耗時的工做,不然會產生應用程序無響應 ANR。考慮到這種狀況,當咱們在服務器拉去一些信息並顯示到 UI 上時,拉去工做咱們將在子線程中進行,拉取完畢以後不能再子線程中直接更新 UI ,沒有 Handler ,那咱們的確沒有辦法將訪問 UI 的工做切換到主線程去執行。所以,系統之因此給咱們提供 Handler,主要緣由是爲了解決在子線程中沒法訪問 UI 的矛盾。
(4)問題:
① 爲何不能再子線程中更新 UI? 答 : 由於 Android 的 UI 控件不是線性安全的。若是在多線程中併發的訪問可能會致使 UI 控件處於不可預期的狀態。 ② 爲何不對 UI 控件的訪問加上鎖機制呢? 答 :首先,加上鎖機制會讓訪問 UI 變的複雜,其次鎖機制會下降 UI 的訪問效率,由於鎖機制會阻塞某些線程的執行。
最簡單最高效的就是採用單線程模型來處理 UI 操做,只需經過 Handlerr 切換一下 UI 訪問的執行線程便可。
Handler 的工做原理
Handler 建立時就會採用當前的 Looper 來構建內部的消息循環系統,若是當前沒有 Looper ,那麼就會報錯。
怎麼解決上述問題?兩個方法:
① 爲當前線程建立 Looper 便可。
② 在 Looper 的線程中建立 Handler 也能夠。
工做原理
(1)Handler 建立過程: Handler 被建立以後,內部的 MessageQueue 和 Looper 就與 Handler 一塊兒工做協同工做了, 而後經過 Handler 的 post 方法將一個 Runnable 投遞給 Handler 內部的 Looper 去處理;也能夠經過 Handler 的 send 方法發送一個消息,也是經過 Looper 去處理的,其實 Post 方法最終也是經過 send 方法來完成的..
(2)send 方法的工做過程:當 Handler 的 send 方法被調用時,它會調用 MessageQueue 的 enqueueMessage 方法將這個消息放到消息隊列中,而後 Looper 發現新消息,就會處理這個消息,最終消息的 Runnable 或者 Handler 的 handlerMessage 方法就會被調用。
2、 Android 的消息機制分析
消息隊列的工做原理
消息隊列在 Android 主要是指 MessageQueue ,MessageQueue 主要包括兩個操做:插入和刪除。消息隊列的內部實現並非隊列,實際上經過一條單鏈表的數據結構來維護消息隊列,單鏈表在插入刪除上頗有優點。
① 插入(enqueueMessage):往消息隊列中插入一條消息。(源碼實現就是單鏈表的插入)
② 刪除(next):從消息隊列中取出一條消息並將其從消息隊列中移除。(next 是一個無限循環的方法,消息隊列沒有信息就處於阻塞狀態,有新消息到來就執行單鏈表的刪除)
Lopper 的工做原理
Looper 在 Android 消息機制中扮演着消息循環的角色,做用:不停地從 MessageQueue 中查看是否有新的消息,若是有消息就會馬上處理,若是沒有消息就會處於阻塞狀態。
(1) Looper 的構造方法
① 建立一個 MessageQueue 消息隊列。
② 將當前線程的對象保存起來。
(2)如何爲一個線程建立 Looper
(Handle 的工做須要 Looper,沒有 Looper 就會報錯)
① 經過 Looper.prepare() 方法爲線程建立一個 Looper 。 ② 經過 Looper.loop() 方法來開啓消息循環。
(3)建立線程的另外一種方法
① 主線程 Looper 的獲取。 Looper 這個方法主要給線程也就是 ActivityThread 建立 Looper 使用的,本質也是經過 prepare 來實現的,因爲主線程的 Looper 比較特殊,因此 Looper 提供了一個 getMainLopper 的方法獲取主線程的 Looper。 ② Looper 的退出。****Looper 提供了兩個方法:quit 方法和 quitSafely 方法。
二者區別:quite 直接退出 Looper。
而 quitSafely 只是設定一個退出標記,先把消息隊列中的消息處理完以後再退出
(4)Looper.loop() 方法實現原理
loop 是一個死循環,惟一能跳出循環的方法就是 MessageQueue 的 next 方法返回了 null。當 Looper 的 quit 方法被調用時,MessageQueue 的 quit 方法或者 quitSafely 方法就會通知消息隊列退出,當消息隊列被標記爲退出狀態時,next 就會返回一個 null。Looper 是必須退出的,不然 loop 會永遠循環下去。
loop 方法會調用 MessageQueue 的 next 方法獲取消息,若是 MessageQueue 沒有消息,next 就會處於阻塞狀態,loop 方法也會處於阻塞狀態。
詳解 Handler 的工做原理
Handler 的主要工做就是發送和接收消息。消息的發送能夠經過 post 一系列方法以及 send 的一系列方法來實現,post 的一系列方法最終都是經過 send 一系列方法來實現的。
代碼實現:
1public class HandlerActivity extends Activity { 2 3 @Override 4 protected void onCreate(@Nullable Bundle savedInstanceState) { 5 super.onCreate(savedInstanceState); 6 setContentView(R.layout.activity_main); 7 8 //開啓線程 9 handler(); 10 } 11 //主線程 12 Handler handler = new Handler(){ 13 14 @Override 15 public void handleMessage(Message msg) { 16 super.handleMessage(msg); 17 switch (msg.what) { 18 case 1: 19 // 獲取Message裏面的複雜數據 20 Bundle date = new Bundle(); 21 date = msg.getData(); 22 String name = date.getString("name"); 23 int age = date.getInt("age"); 24 String sex = date.getString("sex"); 25 //這裏是主線程,可進行對UI的更新 26 textView.setText(name) 27 } 28 } 29 }; 30 31 //子線程 32 public void handler(){ 33 new Thread(new Runnable() { 34 @Override 35 public void run() { 36 Message message = new Message(); 37 message.what = 1; 38 39 // Message對象保存的數據是Bundle類型的 40 Bundle data = new Bundle(); 41 data.putString("name", "李文志"); 42 data.putInt("age", 18); 43 data.putString("sex", "男"); 44 // 把數據保存到Message對象中 45 message.setData(data); 46 // 使用Handler對象發送消息 47 handler.sendMessage(message); 48 } 49 }).start(); 50 } 51}
發送消息
經過對源碼分析,Handler 發送消息的過程僅僅是向消息隊列中插入一條信息,MessageQueue 的 next 方法就會返回這條信息給 Looper ,Looper 接收到消息以後就當即處理,由 Looper 交給 Handler 去處理消息,Handler 的 dispatchMessage 方法就會被調用,這時候 Handler 就進入了消息處理階段。
消息處理
深刻 dispatchMessage 的源代碼進行分析,Handler 處理消息以下:
① 首先檢查 Message 的 callback 是否爲 null, 不爲 null 就經過 handlerCallback 來處理消息。(Message的 callback 是一個 Runnable d 對象,實際上就是 post 方法所遞的 Runnable 參數)
② 其次檢查 mCallback 是否爲 null,不爲 null 就調用 mCallback 的 handlerMessage 方法來處理消息。Callback 是個接口。
③ 咱們經過 Callback 能夠採用以下的方式來建立 Handle 對象。
1Handler handler = new Handler(callback);
這樣建立的意義就是建立一個實例可是並不須要派生 Handler 的子類。
④ 可是,在咱們的平常開發中,常常派生一個 Handler 的子類並重寫其 handleMessage 方法來處理具體的消息,若是不想建立派生子類,就能夠經過 Callback 來實現。
主線程的消息循環
Android 的主線程就是 ActivityThread,主線程的入口方法爲 main ,在 main 方法中系統會經過 Looper.prepareMainLooper();來建立主線程的 Looper 以及 MessageQueue ,並經過 Looper.loop() 來開啓主線程的消息循環。
主線程的消息循環開始了之後,ActivityThread 還須要一個 Handler 來和消息隊列進行交互,這個 Handler 就是 ActivityThread.H 。ActivityThread 經過 ApplicationThread 和 AMS 進行進程間通訊,AMS 以進程間通訊的方式完成 ActvityThread 的請求後會回調 ApplicationThread 中的 Binder 方法,而後 ApplicationThread 向 H 發送消息,H 收到消息會將 ApplicationThread 中的邏輯切換到 ActivityThread 中去執行,即切換到主線程中去執行,這個過程就是主線程的消息循環模型。