Android中Message機制的靈活應用


引用
來自easyandroid論壇,原文:http://www.easyandroid.com/bbs/viewthread.php?tid=33


1.活用Android線程間通信的Message機制

1.1.Message
代碼在frameworks\base\core\java\android\Os\Message.java中。

Message.obtain函數:有多個obtain函數,主要功能一樣,只是參數不一樣。作用是從Message Pool中取出一個Message,如果Message Pool中已經沒有Message可取則新建一個Message返回,同時用對應的參數給得到的Message對象賦值。

Message Pool:大小為10個;通過Message.mPool->(Message並且Message.next)-> (Message並且Message.next)-> (Message並且Message.next)...構造一個Message Pool。Message Pool的第一個元素直接new出來,然后把Message.mPool(static類的static變量)指向它。其他的元素都是使用完的 Message通過Message的recycle函數清理后放到Message Pool(通過Message Pool最后一個Message的next指向需要回收的Message的方式實現)。下圖為Message Pool的結構:


1.2.MessageQueue
MessageQueue里面有一個收到的Message的對列:

MessageQueue.mMessages(static變量)->( Message並且Message.next)-> ( Message並且Message.next)->...,下圖為接收消息的消息隊列:

上層代碼通過Handler的sendMessage等函數放入一個message到MessageQueue里面時最終會調用 MessageQueue的 enqueueMessage函數。enqueueMessage根據上面的接收的Message的隊列的構造把接收到的Message放入隊列中。

MessageQueue的removeMessages函數根據上面的接收的Message的隊列的構造把接收到的Message從隊列中刪除,並且調用對應Message對象的recycle函數把不用的Message放入Message Pool中。

1.3.Looper
Looper對象的創建是通過prepare函數,而且每一個Looper對象會和一個線程關聯

Java代碼   收藏代碼
  1. public static final void prepare() {  
  2.     if (sThreadLocal.get() != null) {  
  3.         throw new RuntimeException("Only one Looper may be created per thread");  
  4.     }  
  5.     sThreadLocal.set(new Looper());  
  6. }  



Looper對象創建時會創建一個MessageQueue,主線程默認會創建一個Looper從而有MessageQueue,其他線程默認是 沒有 MessageQueue的不能接收Message,如果需要接收Message則需要通過prepare函數創建一個MessageQueue。具體操 作請見示例代碼。

Java代碼   收藏代碼
  1. private Looper() {  
  2.     mQueue = new MessageQueue();  
  3.     mRun = true;  
  4.     mThread = Thread.currentThread();  
  5. }  



prepareMainLooper函數只給主線程調用(系統處理,程序員不用處理),它會調用prepare建立Looper對象和MessageQueue。

Java代碼   收藏代碼
  1. public static final void prepareMainLooper() {  
  2.     prepare();  
  3.     setMainLooper(myLooper());  
  4.     if (Process.supportsProcesses()) {  
  5.         myLooper().mQueue.mQuitAllowed = false;  
  6.     }  
  7. }  



Loop函數從MessageQueue中從前往后取出Message,然后通過Handler的dispatchMessage函數進行消息的 處理(可見消息的處理是Handler負責的),消息處理完了以后通過Message對象的recycle函數放到Message Pool中,以便下次使用,通過Pool的處理提供了一定的內存管理從而加速消息對象的獲取。至於需要定時處理的消息如何做到定時處理,請見 MessageQueue的next函數,它在取Message來進行處理時通過判斷MessageQueue里面的Message是否符合時間要求來決 定是否需要把Message取出來做處理,通過這種方式做到消息的定時處理。

Java代碼   收藏代碼
  1. public static final void loop() {  
  2.     Looper me = myLooper();  
  3.     MessageQueue queue = me.mQueue;  
  4.     while (true) {  
  5.         Message msg = queue.next(); // might block  
  6.         //if (!me.mRun) {  
  7.         //    break;  
  8.         //}  
  9.         if (msg != null) {  
  10.             if (msg.target == null) {  
  11.                 // No target is a magic identifier for the quit message  
  12.                 return;  
  13.             }  
  14.   
  15.             if (me.mLogging!= null)   
  16.                 me.mLogging.println(">>>>> Dispatching to " + msg.target + " "+ msg.callback + ": " + msg.what);  
  17.             msg.target.dispatchMessage(msg);  
  18.             if (me.mLogging!= null)   
  19.                 me.mLogging.println("<<<<< Finished to" + msg.target + " "+ msg.callback);  
  20.             msg.recycle();  
  21.         }  
  22.     }  
  23. }  



1.4.Handler

Handler的構造函數表示Handler會有成員變量指向Looper和MessageQueue,后面我們會看到沒什么需要這些引用;至於callback是實現了Callback接口的對象,后面會看到這個對象的作用。

Java代碼   收藏代碼
  1. public Handler(Looper looper, Callback callback) {  
  2.     mLooper = looper;  
  3.     mQueue = looper.mQueue;  
  4.     mCallback = callback;  
  5. }  
  6.   
  7. public interface Callback {  
  8.     public boolean handleMessage(Message msg);  
  9. }  



獲取消息:直接通過Message的obtain方法獲取一個Message對象。

Java代碼   收藏代碼
  1. public final Message obtainMessage(int what, int arg1, int arg2, Object obj){  
  2.     return Message.obtain(this, what, arg1, arg2, obj);  
  3. }  



發送消息:通過MessageQueue的enqueueMessage把Message對象放到MessageQueue的接收消息隊列中

Java代碼   收藏代碼
  1. public boolean sendMessageAtTime(Message msg, long uptimeMillis){  
  2.     boolean sent = false;  
  3.     MessageQueue queue = mQueue;  
  4.     if (queue != null) {  
  5.         msg.target = this;  
  6.     sent = queue.enqueueMessage(msg, uptimeMillis);  
  7.     } else {  
  8.         RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");  
  9.         Log.w("Looper", e.getMessage(), e);  
  10.     }  
  11.     return sent;  
  12. }  




線程如何處理MessageQueue中接收的消息:在Looper的loop函數中循環取出MessageQueue的接收消息隊列中的消息, 然后調用 Hander的dispatchMessage函數對消息進行處理,至於如何處理(相應消息)則由用戶指定(三個方法,優先級從高到低:Message里 面的Callback,一個實現了Runnable接口的對象,其中run函數做處理工作;Handler里面的mCallback指向的一個實現了 Callback接口的對象,里面的handleMessage進行處理;處理消息Handler對象對應的類繼承並實現了其中 handleMessage函數,通過這個實現的handleMessage函數處理消息)。

Java代碼   收藏代碼
  1. public void dispatchMessage(Message msg) {  
  2.     if (msg.callback != null) {  
  3.         handleCallback(msg);  
  4.     } else {  
  5.         if (mCallback != null) {  
  6.             if (mCallback.handleMessage(msg)) {  
  7.                 return;  
  8.             }  
  9.         }  
  10.         handleMessage(msg);  
  11.     }  
  12. }  



Runnable說明:Runnable只是一個接口,實現了這個接口的類對應的對象也只是個普通的對象,並不是一個Java中的Thread。Thread類經常使用Runnable,很多人有誤解,所以這里澄清一下。


從上可知以下關系圖:

其中清理Message是Looper里面的loop函數指把處理過的Message放到Message的Pool里面去,如果里面已經超過最大值10個,則丟棄這個Message對象。

調用Handler是指Looper里面的loop函數從MessageQueue的接收消息隊列里面取出消息,然后根據消息指向的Handler對象調用其對應的處理方法。
1.5.代碼示例

下面我們會以android實例來展示對應的功能,程序界面於下:

程序代碼如下,后面部分有代碼說明:

Java代碼   收藏代碼
  1. package com.android.messageexample;  
  2. import android.app.Activity;  
  3. import android.content.Context;  
  4. import android.graphics.Color;  
  5. import android.os.Bundle;  
  6. import android.os.Handler;  
  7. import android.os.Looper;  
  8. import android.os.Message;  
  9. import android.util.Log;  
  10. import android.view.View;  
  11. import android.view.View.OnClickListener;  
  12. import android.widget.Button;  
  13. import android.widget.LinearLayout;  
  14. import android.widget.TextView;  
  15. public class MessageExample extends Activity implements OnClickListener {  
  16.  private final int WC = LinearLayout.LayoutParams.WRAP_CONTENT;  
  17.     private final int FP = LinearLayout.LayoutParams.FILL_PARENT;  
  18.     public TextView tv;  
  19.     private EventHandler mHandler;  
  20.     private Handler mOtherThreadHandler=null;  
  21.     private Button btn, btn2, btn3, btn4, btn5, btn6;  
  22.     private NoLooperThread noLooerThread = null;  
  23.     private OwnLooperThread ownLooperThread = null;  
  24.     private ReceiveMessageThread receiveMessageThread =null;  
  25.     private Context context = null;  
  26.     private final String sTag = "MessageExample";  
  27.     private boolean postRunnable = false;  
  28.    
  29.  /** Called when the activity is first created. */  
  30.  @Override  
  31.     public void onCreate(Bundle savedInstanceState) {  
  32.         super.onCreate(savedInstanceState);  
  33.         context = this.getApplicationContext();  
  34.         LinearLayout layout = new LinearLayout(this);  
  35.         layout.setOrientation(LinearLayout.VERTICAL);  
  36.         btn = new Button(this);  
  37.         btn.setId(101);  
  38.         btn.setText("message from main thread self");  
  39.         btn.setOnClickListener(this);  
  40.         LinearLayout.LayoutParams param =  
  41.             new LinearLayout.LayoutParams(250,50);  
  42.         param.topMargin = 10;  
  43.         layout.addView(btn, param);  
  44.         btn2 = new Button(this);  
  45.         btn2.setId(102);  
  46.         btn2.setText("message from other thread to main thread");  
  47.         btn2.setOnClickListener(this);  
  48.         layout.addView(btn2, param);  
  49.         btn3 = new Button(this);  
  50.         btn3.setId(103);  
  51.         btn3.setText("message to other thread from itself");  
  52.         btn3.setOnClickListener(this);  
  53.         layout.addView(btn3, param);  
  54.         btn4 = new Button(this);  
  55.         btn4.setId(104);  
  56.         btn4.setText("message with Runnable as callback from other thread to main thread");  
  57.         btn4.setOnClickListener(this);  
  58.         layout.addView(btn4, param);  
  59.         btn5 = new Button(this);  
  60.         btn5.setId(105);  
  61.         btn5.setText("main thread's message to other thread");  
  62.         btn5.setOnClickListener(this);  
  63.         layout.addView(btn5, param);  
  64.         btn6 = new Button(this);  
  65.         btn6.setId(106);  
  66.         btn6.setText("exit");  
  67.         btn6.setOnClickListener(this);  
  68.         layout.addView(btn6, param);  
  69.         tv = new TextView(this);  
  70.         tv.setTextColor(Color.WHITE);  
  71.         tv.setText("");  
  72.         LinearLayout.LayoutParams param2 =  
  73.            new LinearLayout.LayoutParams(FP, WC);  
  74.         param2.topMargin = 10;  
  75.         layout.addView(tv, param2);  
  76.         setContentView(layout);       
  77.           
  78.         //主線程要發送消息給other thread, 這里創建那個other thread  
  79.   receiveMessageThread = new ReceiveMessageThread();  
  80.   receiveMessageThread.start();  
  81.     }  
  82.    
  83.  //implement the OnClickListener interface  
  84.  @Override  
  85.  public void onClick(View v) {  
  86.   switch(v.getId()){  
  87.   case 101:  
  88.    //主線程發送消息給自己  
  89.    Looper looper;  
  90.    looper = Looper.myLooper();  //get the Main looper related with the main thread  
  91.    //如果不給任何參數的話會用當前線程對應的Looper(這里就是Main Looper)為Handler里面的成員mLooper賦值  
  92.    mHandler = new EventHandler(looper);   
  93.    //mHandler = new EventHandler();  
  94.    // 清除整個MessageQueue里的消息  
  95.    mHandler.removeMessages(0);  
  96.    String obj = "This main thread's message and received by itself!";  
  97.    //得到Message對象  
  98.    Message m = mHandler.obtainMessage(111, obj);  
  99.    // 將Message對象送入到main thread的MessageQueue里面  
  100.    mHandler.sendMessage(m);  
  101.    break;  
  102.   case 102:      
  103.    //other線程發送消息給主線程  
  104.    postRunnable = false;  
  105.    noLooerThread = new NoLooperThread();  
  106.    noLooerThread.start();  
  107.    break;  
  108.   case 103:    
  109.    //other thread獲取它自己發送的消息  
  110.    tv.setText("please look at the error level log for other thread received message");  
  111.    ownLooperThread = new OwnLooperThread();  
  112.    ownLooperThread.start();  
  113.    break;   
  114.   case 104:       
  115.    //other thread通過Post Runnable方式發送消息給主線程  
  116.    postRunnable = true;  
  117.    noLooerThread = new NoLooperThread();  
  118.    noLooerThread.start();  
  119.    break;  
  120.   case 105:       
  121.    //主線程發送消息給other thread  
  122.    if(null!=mOtherThreadHandler){  
  123.     tv.setText("please look at the error level log for other thread received message from main thread");  
  124.     String msgObj = "message from mainThread";  
  125.     Message mainThreadMsg = mOtherThreadHandler.obtainMessage(111, msgObj);  
  126.     mOtherThreadHandler.sendMessage(mainThreadMsg);  
  127.    }  
  128.    break;  
  129.   case 106:  
  130.    finish();  
  131.    break;  
  132.   }  
  133.  }  
  134.  class EventHandler extends Handler  
  135.  {  
  136.   public EventHandler(Looper looper) {  
  137.    super(looper);  
  138.   }  
  139.   public EventHandler() {  
  140.    super();  
  141.   }  
  142.   public void handleMessage(Message msg) {  
  143.    //可以根據msg.what執行不同的處理,這里沒有這么做  
  144.    switch(msg.what){  
  145.    case 1:  
  146.     tv.setText((String)msg.obj);  
  147.     break;  
  148.    case 2:  
  149.     tv.setText((String)msg.obj);  
  150.     noLooerThread.stop();  
  151.     break;  
  152.    case 3:  
  153.     //不能在非主線程的線程里面更新UI,所以這里通過Log打印收到的消息  
  154.     Log.e(sTag, (String)msg.obj);  
  155.     ownLooperThread.stop();  
  156.     break;  
  157.    default:  
  158.     //不能在非主線程的線程里面更新UI,所以這里通過Log打印收到的消息  
  159.     Log.e(sTag, (String)msg.obj);  
  160.     break;  
  161.    }  
  162.   }  
  163.  }  
  164.  //NoLooperThread  
  165.  class NoLooperThread extends Thread{  
  166.   private EventHandler mNoLooperThreadHandler;  
  167.   public void run() {  
  168.    Looper myLooper, mainLooper;  
  169.    myLooper = Looper.myLooper();  
  170.    mainLooper = Looper.getMainLooper();    //這是一個static函數  
  171.    String obj;  
  172.    if(myLooper == null){  
  173.     mNoLooperThreadHandler = new EventHandler(mainLooper);  
  174.     obj = "NoLooperThread has no looper and handleMessage function executed in main thread!";  
  175.    }  
  176.    else {  
  177.     mNoLooperThreadHandler = new EventHandler(myLooper);  
  178.     obj = "This is from NoLooperThread self and handleMessage function executed in NoLooperThread!";  
  179.    }  
  180.    mNoLooperThreadHandler.removeMessages(0);  
  181.    if(false == postRunnable){  
  182.     //send message to main thread  
  183.     Message m = mNoLooperThreadHandler.obtainMessage(211, obj);  
  184.     mNoLooperThreadHandler.sendMessage(m);  
  185.     Log.e(sTag, "NoLooperThread id:" + this.getId());  
  186.    }else{  
  187.     //下面new出來的實現了Runnable接口的對象中run函數是在Main Thread中執行,不是在NoLooperThread中執行  
  188.     //注意Runnable是一個接口,它里面的run函數被執行時不會再新建一個線程  
  189.     //您可以在run上加斷點然后在eclipse調試中看它在哪個線程中執行  
  190.     mNoLooperThreadHandler.post(new Runnable(){    
  191.      @Override    
  192.      public void run() {    
  193.       tv.setText("update UI through handler post runnalbe mechanism!");  
  194.       noLooerThread.stop();  
  195.      }    
  196.     });    
  197.    }  
  198.   }  
  199.  }  
  200.    
  201.  //OwnLooperThread has his own message queue by execute Looper.prepare();  
  202.  class OwnLooperThread extends Thread{  
  203.   private EventHandler mOwnLooperThreadHandler;  
  204.   public void run() {  
  205.    Looper.prepare();   
  206.    Looper myLooper, mainLooper;  
  207.    myLooper = Looper.myLooper();  
  208.    mainLooper = Looper.getMainLooper();    //這是一個static函數  
  209.    String obj;  
  210.    if(myLooper == null){  
  211.     mOwnLooperThreadHandler = new EventHandler(mainLooper);  
  212.     obj = "OwnLooperThread has no looper and handleMessage function executed in main thread!";  
  213.    }  
  214.    else {  
  215.     mOwnLooperThreadHandler = new EventHandler(myLooper);  
  216.     obj = "This is from OwnLooperThread self and handleMessage function executed in NoLooperThread!";  
  217.    }  
  218.    mOwnLooperThreadHandler.removeMessages(0);  
  219.    //給自己發送消息  
  220.    Message m = mOwnLooperThreadHandler.obtainMessage(311, obj);  
  221.    mOwnLooperThreadHandler.sendMessage(m);  
  222.    Looper.loop();   
  223.   }  
  224.  }  
  225.    
  226.  //ReceiveMessageThread has his own message queue by execute Looper.prepare();  
  227.  class ReceiveMessageThread extends Thread{  
  228.   public void run() {  
  229.    Looper.prepare();  
  230.    mOtherThreadHandler = new Handler(){  
  231.     public void handleMessage(Message msg) {  
  232.      Log.e(sTag, (String)msg.obj);  
  233.     }  
  234.    };  
  235.    Looper.loop();  
  236.   }  
  237.  }  
  238.    
  239. }  


說明(代碼詳細解釋請見后文):

使用Looper.myLooper靜態方法可以取得當前線程的Looper對象。

使用mHandler = new EevntHandler(Looper.myLooper()); 可建立用來處理當前線程的Handler對象;其中,EevntHandler是Handler的子類。

使用mHandler = new EevntHandler(Looper.getMainLooper()); 可建立用來處理main線程的Handler對象;其中,EevntHandler是Handler的子類。


1.5.1.主線程給自己發送消息示例

主線程發送消息:

在onClick的case 101中創建一個繼承自Handler的EventHandler對象,然后獲取一個消息,然后通過EventHandler對象調用 sendMessage把消息發送到主線程的MessageQueue中。主線程由系統創建,系統會給它建立一個Looper對象和 MessageQueue,所以可以接收消息。這里只要根據主線程的Looper對象初始化EventHandler對象,就可以通過 EventHandler對象發送消息到主線程的消息隊列中。

主線程處理消息:

這里是通過EventHandler的handleMessage函數處理的,其中收到的Message對象中what值為一的消息就是發送給它的,然后把消息里面附帶的字符串在TextView上顯示出來。


1.5.2.其他線程給主線程發送消息示例

其他線程發送消息(這里是說不使用Runnable作為callback的消息):

首先 postRunnable設為false,表示不通過Runnable方式進行消息相關的操作。然后啟動線程noLooerThread,然后以主線程的 Looper對象為參數建立EventHandler的對象mNoLooperThreadHandler,然后獲取一個Message並把一個字符串賦 值給它的一個成員obj,然后通過mNoLooperThreadHandler把消息發送到主線程的MessageQueue中。

主線程處理消息:

這里是通過EventHandler的handleMessage函數處理的,其中收到的Message對象中what值為二的消息就是上面發送給它的,然后把消息里面附帶的字符串在TextView上顯示出來。

1.5.3.其他線程給自己發送消息示例

其他線程發送消息:

其他非主線程建立后沒有自己的Looper對象,所以也沒有MessageQueue,需要給非主線程發送消息時需要建立 MessageQueue以便接收消息。下面說明如何給自己建立MessageQueue和Looper對象。從OwnLooperThread的run 函數中可以看見有一個 Looper.prepare()調用,這個就是用來建立非主線程的MessageQueue和Looper對象的。

所以這里的發送消息過程是建立線程mOwnLooperThread,然后線程建立自己的Looper和MessageQueue對象,然后根據 上面建立的Looper對象建立對應的EventHandler對象mOwnLooperThreadHandler,然后由 mOwnLooperThreadHandler建立消息並且發送到自己的MessageQueue里面。

其他線程處理接收的消息:

線程要接收消息需要在run函數中調用Looper.loop(),然后loop函數會從MessageQueue中取出消息交給對應的 Handler對象 mOwnLooperThreadHandler處理,在mOwnLooperThreadHandler的handleMessage函數中會把 Message對象中what值為三的消息(上面發送的消息)在Log中打印出來,可以通過Logcat工具查看log。

1.5.4.其他線程以Runnable為消息參數給主線程發送消息示例

其他線程發送消息(這里是說使用Runnable作為callback的消息):

首先 postRunnable設為true,表示通過Runnable方式進行消息相關的操作。然后啟動線程noLooerThread,然后以主線程的 Looper對象為參數建立EventHandler的對象mNoLooperThreadHandler,然后獲取一個Message並把一個字符串賦 值給它的一個成員obj,然后通過mNoLooperThreadHandler把消息發送到主線程的MessageQueue中。

主線程處理消息:

主線程收到上面發送的Message后直接運行上面Runnable對象中的run函數進行相應的操作。run函數通過Log打印一個字符串,可以通過Logcat工具查看log。


1.5.5.主線程給其他線程發送消息示例

主線程發送消息:

這里首先要求線程receiveMessageThread運行(在onCreate函數中完成),並且准備好自己的Looper和 MessageQueue(這個通過ReceiveMessageThread中的run函數中的Looper.prepare()調用完成),然后根據 建立的Looper對象初始化Handler對象mOtherThreadHandler。然后在onClick的case 105中由mOtherThreadHandler建立一個消息(消息中有一個字符串對象)並且發送到線程receiveMessageThread中的 MessageQueue中。

其他線程處理接收的消息:

線程要接收消息需要在run函數中調用Looper.loop(),然后loop函數會從MessageQueue中取出消息交給對應的 Handler對象mOtherThreadHandler處理,在mOtherThreadHandler的handleMessage函數中會把 Message對象中的字符串對象在Log中打印出來,可以通過Logcat工具查看log。

 

轉自:http://qaohao.iteye.com/blog/509145


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM