Android訊息機制Message訊息池

yangxi_001發表於2016-12-29

這篇文章主要說下Android在實現Message類的時候使用的一個所謂的訊息池的問題。

我們在使用Android的訊息迴圈時,一般按照下面的方式使用,為了使執行緒具有訊息迴圈如下:

//實現自己的Handler類,重寫handlerMessage()方法

[java] view plain copy
  1. private class MyHandler extends Handler{  
  2.     @Override  
  3.     public void handleMessage(Message msg) {  
  4.         if(msg.what==1){  
  5.             textView.setText(""+msg.arg1);//獲得傳遞過來的資料  
  6.         }  
  7.         super.handleMessage(msg);  
  8.     }     
  9. }  


線上程的run()方法裡呼叫Looper.prepare(),例項化一個Handler物件,呼叫Looper.loop()使執行緒進入訊息迴圈

[java] view plain copy
  1. public void run(){  
  2.     Looper.prepare();  
  3.     //dosomething else  
  4.    handler=new MyHandler();  
  5.    Looper.loop();  
  6. }  

Handler物件的例項話必須在Looper.prepare()之後。

當我們要給具有訊息迴圈的執行緒傳送訊息時,我們先要獲得具有訊息迴圈的執行緒的 Handler 物件(或者先獲取具有訊息迴圈的執行緒的Looper物件,再使用這個Looper物件構造Handler物件),構造一個Message物件,然後呼叫Handler物件的sendMessage方法

[java] view plain copy
  1. Message message=Message.obtain();  
  2. message.what=1;  
  3. message.arg1=count;  
  4. handler.sendMessage(message);  

 

說了這麼多,現在就來說下Message裡的訊息池問題,我們先看Message的靜態成員方法 Message.obtain();


 

[java] view plain copy
  1. // sometimes we store linked lists of these things  
  2. /*package*/ Message next;  
  3. private static final Object sPoolSync = new Object();  
  4. private static Message sPool;  
  5. private static int sPoolSize = 0;  
  6. private static final int MAX_POOL_SIZE = 50;  
  7. /** 
  8.  * Return a new Message instance from the global pool. Allows us to 
  9.  * avoid allocating new objects in many cases. 
  10.  */  
  11. public static Message obtain() {  
  12.     synchronized (sPoolSync) {  
  13.         if (sPool != null) {  
  14.             Message m = sPool;  
  15.             sPool = m.next;  
  16.             m.next = null;  
  17.             sPoolSize--;  
  18.             return m;  
  19.         }  
  20.     }  
  21.     return new Message();  
  22. }  

在這個類中sPool代表這個訊息池的頭訊息,sPoolSize表示訊息池中可用的訊息的個數即沒有被使用的Message物件的個數,next表示下一個可用的訊息Message物件。
可以看到obtain()方法說會從全域性訊息池中取訊息,假設是第一次獲得一個Message物件,那麼sPool肯定為null,也就說第一次獲取訊息Message物件時是還沒有訊息池的,必須通過Message的構造方法獲取一個Message物件的,Message的構造方法什麼也沒幹

[java] view plain copy
  1. /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}). 
  2. */  
  3. public Message() {  
  4. }  

那這就有點奇怪了,它的訊息池是什麼時候初始化呢?難道不是先new幾個Message物件然後存著?這是我們注意到Message類的另一個成員方法recycle(),注意這個方法不是靜態的.

[java] view plain copy
  1. /** 
  2.  * Return a Message instance to the global pool.  You MUST NOT touch 
  3.  * the Message after calling this function -- it has effectively been 
  4.  * freed. 
  5.  */  
  6. public void recycle() {  
  7.     clearForRecycle();  
  8.   
  9.     synchronized (sPoolSync) {  
  10.         if (sPoolSize < MAX_POOL_SIZE) {  
  11.             next = sPool;  
  12.             sPool = this;  
  13.             sPoolSize++;  
  14.         }  
  15.     }  
  16. }  

 

裡面的clearForRecycle()方法只是把一些成員變數置為null,以便垃圾回收

[java] view plain copy
  1. /*package*/ void clearForRecycle() {  
  2.     flags = 0;  
  3.     what = 0;  
  4.     arg1 = 0;  
  5.     arg2 = 0;  
  6.     obj = null;  
  7.     replyTo = null;  
  8.     when = 0;  
  9.     target = null;  
  10.     callback = null;  
  11.     data = null;  
  12. }  

 

從這裡我們就可以發現,訊息池中Message的物件是通過recycle()放進去的. 但是我們自己並沒有呼叫recycle()方法,那這個方法是在哪裡呼叫的?看下Looper的原始碼就知道,在Looper的loop()方法的最後呼叫了Message物件msg的recycle()方法來回收這個Message物件,通過recycle()將這個Message物件的資料清空然後連結到訊息池中(採用的頭插法)。

相關文章