Android 高級進階之深入剖析消息機制

Android 高級進階之深入剖析消息機制

閱讀時間:8 分鐘

坐穩了沒?要開車了哦


寫在前邊

今天分享的內容是深入剖析 Android 的消息機制,有些人問了,一些功能會用不就行了嗎?爲什麼還要分析底層源碼呢?今天小鹿告訴你的是很多開源的項目都已經不需要我們造輪子了,重複造輪子是多麼愚蠢的一件事。但是,Android 的底層源碼和一些功能的實現讓我們學習到底層的模式和邏輯實現。

學編程什麼最重要,當然是邏輯思維了,即使你什麼功能都能實現,邏輯思維能力差照樣啥都幹不了。你的思維邏輯能力差,在技術路線上已經決定了你的高度。

Android 的消息機制

Android 的消息機制主要是指 Handlerr 的運行需要底層的 MessageQueue 和 Looper 的支撐。

(1)MessageQueue 的中文翻譯是消息隊列。以隊列的形式對外提供插入和刪除工作。雖然叫做消息隊列,但是內部存儲結構並不是真正的隊列,而是以單鏈表的數據結構來存儲消息列表。

(2)Looper 的中文翻譯爲循環,我們叫它消息循環。由於 MessageQueue 只是一個存儲單元,不會去處理消息。而 Looper 確彌補了這個功能,Looper 會以無限無限循環的形式去查找是否有新的消息,有的話就去處理消息,否則就一直等待。

學習思維導圖:

以後文章中的思維導圖是小鹿給大家精心整理的,這樣對每篇分享的文章都有一個清晰地結構,有利於複習和整理。

Android 高級進階之深入剖析消息機制

一、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 方法就會被調用。

Android 高級進階之深入剖析消息機制

二、 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 就進入了消息處理階段。

消息處理

Android 高級進階之深入剖析消息機制

深入 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 中去執行,即切換到主線程中去執行,這個過程就是主線程的消息循環模型。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章