Android應用程式訊息處理機制

pswyjz發表於2021-09-09

原文連結:http://www.apkbus.com/blog-705730-61240.html


Android的訊息處理機制主要分為四個部分:

·         建立訊息佇列

·         訊息迴圈

·         訊息傳送

·         訊息處理

主要涉及三個類:

·         MessageQueue

·         Looper

·         Handler

Android應用程式每啟動一個執行緒,都為其建立一個訊息佇列,然後進入到一個無限迴圈之中。然後不斷檢查佇列中是否有新訊息需要處理。如果沒有,執行緒就會進入睡眠狀態,反之會對訊息進行分發處理。

下面根據上面所說的進行詳述。

建立訊息佇列

整個建立過程涉及到兩個類:MessageQueue 和 Looper。它們在C++層有兩個對應的類:NativeMessageQueue和Looper。其關係如下圖所示:

1

2

3

4

5

6

7

8

9

10

11

12

      +------------+       +------+

      |MessageQueue+----^+Looper|

      +-----+------+       +------+

            |                    

            |                    

            |                    

+-----------+------+       +------+

|NativeMessageQueue+^----+Looper|

+------------------+       +------+

 

        A----^B表示B中儲存A的引用

 

建立過程如下所示:

1.       Looper的prepare或者prepareMainLooper靜態方法被呼叫,將一個Looper物件儲存在ThreadLocal裡面。

2.      Looper物件的初始化方法裡,首先會新建一個MessageQueue物件。

3.      MessageQueue物件的初始化方法透過JNI初始化C++層的NativeMessageQueue物件。

4.      NativeMessageQueue物件在建立過程中,會初始化一個C++層的Looper物件。

5.      C++層的Looper物件在建立的過程中,會在內部建立一個管道(pipe),並將這個管道的讀寫fd都儲存在mWakeReadPipeFd和mWakeWritePipeFd中。
然後新建一個epoll例項,並將兩個fd註冊進去。

6.      利用epoll的機制,可以做到當管道沒有訊息時,執行緒睡眠在讀端的fd上,當其他執行緒往管道寫資料時,本執行緒便會被喚醒以進行訊息處理。

訊息迴圈

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

          +------+    +------------+  +------------------+  +--------------+                    

          |Looper|    |MessageQueue|  |NativeMessageQueue|  |Looper(Native)|                    

          +--+---+    +------+-----+  +---------+--------+  +-------+------+                    

               |                 |                  |                     |                            

               |                 |                  |                     |                            

+-------------------------------------------------------------------------------+                

|[msg   loop]  |     next()      |                  |                     |             |                

|            +------------>  |                  |                     |             |                

|            |                 |                  |                     |             |                

|            |                 |                  |                     |             |                

|            |                 | nativePollOnce() |                     |             |                

|            |                 |    pollOnce()    |                     |             |                

|            |                 +----------------> |                     |             |                

|            |                 |                  |                     |             |              

|            |                 |                  |                     |             |                

|            |                 |                  |                     |             |                

|            |                 |                  |                     |             |                

|            |                 |                  |       pollOnce()    |             |                

|            |                 |                  +-----------------> |             |                

|            |                 |                  |                     |             |                

|            |                 |                  |                     | epoll_wait()              

|            |                 |                  |                     +--------+  |                

|            |                 |                  |                     |        |  |                

|            |                 |                  |                     |        |  |                

|            |                 |                  |                     |

|            |                 |                  |                     | awoken()  |                

|            +                 +                  +                     +             |                

|                                                                                 |                

|                                                                                 |                

+-------------------------------------------------------------------------------+                

 

1.       首先透過呼叫Looper的loop方法開始訊息監聽。loop方法裡會呼叫MessageQueue的next方法。next方法會堵塞執行緒直到有訊息到來為止。

2.      next方法透過呼叫nativePollOnce方法來監聽事件。next方法內部邏輯如下所示(簡化):
a. 進入死迴圈,以引數timout=0呼叫nativePollOnce方法。
b. 如果訊息佇列中有訊息,nativePollOnce方法會將訊息儲存在mMessage成員中。nativePollOnce方法返回後立刻檢查mMessage成員是否為空。
c. 如果mMessage不為空,那麼檢查它指定的執行時間。如果比當前時間要前,那麼馬上返回這個mMessage,否則設定timeout為兩者之差,進入下一次迴圈。
d. 如果mMessage為空,那麼設定timeout為-1,即下次迴圈nativePollOnce永久堵塞。

3.      nativePollOnce方法內部利用epoll機制在之前建立的管道上等待資料寫入。接收到資料後馬上讀取並返回結果。

訊息傳送

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

          +-------+       +------------+     +------------------+     +--------------+                        

          |Handler|       |MessageQueue|     |NativeMessageQueue|     |Looper(Native)|                        

          +--+----+       +-----+------+     +---------+--------+     +-------+------+                        

               |                |                    |                    |                                

               |                |                    |                    |                                

sendMessage()|                |                    |                    |                                

+----------> |                |                    |                    |                                

               |                |                    |                    |                                

               |enqueueMessage()|                    |                    |                                

               +--------------> |                    |                    |                                

               |                |                    |                    |                                

               |                |                    |                    |                                

               |                |                    |                    |                                

               |                |  nativeWake()      |                    |                                

               |                |    wake()          |                    |                                

               |                +------------------> |                    |                                

               |                |                    |                    |                                

               |                |                    |    wake()          |                                

               |                |                    +------------------> |                                

               |                |                    |                    |                                

               |                |                    |                    |                                

               |                |                    |                    |write(mWakeWritePipeFd, "W", 1)

               |                |                    |                    |                                

               |                |                    |                    |                                

               |                |                    |                    |                                

               |                |                    |                    |                                

               |                |                    |                    |                                

               +                +                    +                    +                                

 

訊息傳送過程主要由Handler物件來驅動。

1.       Handler物件在建立時會儲存當前執行緒的looper和MessageQueue,如果傳入Callback的話也會儲存起來。

2.      使用者呼叫handler物件的sendMessage方法,傳入msg物件。handler透過呼叫MessageQueue的enqueueMessage方法將訊息壓入MessageQueue。

3.      enqueueMessage方法會將傳入的訊息物件根據觸發時間(when)插入到message queue中。然後判斷是否要喚醒等待中的佇列。
a. 如果插在佇列中間。說明該訊息不需要馬上處理,不需要由這個訊息來喚醒佇列。
b. 如果插在佇列頭部(或者when=0),則表明要馬上處理這個訊息。如果當前佇列正在堵塞,則需要喚醒它進行處理。

4.      如果需要喚醒佇列,則透過nativeWake方法,往前面提到的管道中寫入一個”W”字元,令nativePollOnce方法返回。

訊息處理

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

               +------+         +-------+                                                                    

               |Looper|         |Handler|                                                                    

               +--+---+         +---+---+                                                                    

                |                 |                                                                        

                |                 |                                                                        

    loop()      |                 |                                                                        

    [after   next()]              |                                                                        

    +---------> |                 |                                                                        

                |                 |                                                                        

                |dispatchMessage()                                                                      

                +-------------> |                                                                        

                |                 |                                                                        

                |                 |                                                                        

                |                 | handleMessage()                                                        

                |                 +-------+                                                                

                |                 |         |                                                                

                |                 |         |                                                                

                |                 |

                |                 |     (callback   or subclass)                                              

                |                 |                                                                        

                +                 +                                                                        

 

Looper物件的loop方法裡面的queue.next方法如果返回了message,那麼handler的dispatchMessage會被呼叫。
a. 如果新建Handler的時候傳入了callback例項,那麼callback的handleMessage方法會被呼叫。
b. 如果是透過post方法向handler傳入runnable物件的,那麼runnable物件的run方法會被呼叫。
c. 其他情況下,handler方法的handleMessage會被呼叫。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/755/viewspace-2814843/,如需轉載,請註明出處,否則將追究法律責任。

相關文章