Android開發之訊息處理機制(二)——訊息迴圈

來源:互聯網
上載者:User

 

Android開發之訊息處理機制(二)——訊息迴圈

/*

 *  Android開發之訊息處理機制(二)——訊息迴圈

 *  北京Android俱樂部群:167839253  

 *  Created on: 2011-9-1

 *  Author: blueeagle

 *  Email: liujiaxiang@gmail.com

 */

 

       先介紹一下如何在Android應用程式中使用後台線程和UI線程。

建立Android友好的後台線程時,最靈活的方式就是建立Handler子類的一個執行個體。每個Activity僅需要一個Handler對象,而且不需要手動註冊他。

       後台線程可以與Handler通訊,Handler將在Activity的UI線程上執行自己所有的工作。這一點非常重要。因為UI的更改,只能發生在Activity的UI線程上。

       有兩種與Handler通訊的方式:訊息和Runnable對象。

       關於訊息:

       要向Handler發送一個Message,首先調用obtainMessage()從池中擷取Message對象。obtainMessage()對象有許多特點,允許建立空的Message對象或填充了訊息標示符和參數的對象。需要的Handler處理過程越複雜,就越需要將資料放在Message中,以協助Handler區分不同的事件。

       然後可以通過訊息佇列將Message發送給Handler,此時需要使用sendMessage…()系列方法中的一個方法。

       sendMessage();立即將訊息放在隊列中。

       sendMessageAtFrontOfQueue();立即將訊息放在隊列中,並將其放在訊息佇列的最前面,這樣該訊息就會具有比所有其他訊息更高的優先順序。

       sendMessageAtTime();在規定的時間將訊息放在隊列中,這個時間用ms表示,基於系統正常工作時間。(SystemClock.uptimeMillis())

       sendMessageDelayed();在一段延遲之後將訊息放在隊列中,延遲用ms表示。

       要處理這些訊息,Handler需要實現handleMessage(),將使用出現在訊息佇列中的每個訊息來調用handleMessage()。此處Handler可以根據需要更新UI。但是,它仍然應該迅速完成此工作,因為在它完成之前,其他UI工作會暫停。

       關於Runnable :

       如果不願意使用Message對象,也可以將Runnable對象傳遞給Handler,Handler將在ActivityUI線程上運行這些Runnable對象。Handler提供了一組post…()方法來傳入Runnable對象供最終處理。

       對Android的訊息迴圈機制的理解,可以仿照Windows訊息迴圈的機制進行。

每個Handler都會和一個線程和線程的message queue關聯起來,此時你可以傳遞messages 和 runnable對象到message queue中。後面可以從message queue中拿出該對象並且執行。Handler, Message queue這兩個對象之間的互動,就涉及到了Looper這個東西。

關於Handler,Looper,Message Queue三者之間的關係,如所示:

Handler有很多建構函式

Handler在無參數的構造方法中調用Looper.myLooper()方法,裡面就是從當前線程裡面擷取一個Looper的同時一併初始化MessageQueue,並且從中得到looper的MessageQueue。可以看出Handler就是Looper和MessageQueue的管理者和調度者。

       其中最重要的是sendMessageAtTime方法,當你往Handler中發送Message訊息的時候,從代碼看出他自己並不去處理Message,而是交給了MessageQueue,由queue.enqueueMessage來處理。

       Looper代碼中有一個prepare()方法,通過ThreadLocal實現一個線程只有一個Looper。

Handler的訊息發送

Handler是一個核心,負責message的建立,獲得,處理。Handler的sendMessage()方法最終其實就是調用了queue.enqueueMessage(msg, uptimeMillis);把訊息放入message queue中。Looper通過Looper.loop()檢測到有訊息並將訊息廣播後,Handler又負責接收到此訊息並調用handleMessage進行處理接下來的事情。Message的建立一般都是通過如下代碼建立的,把Handler傳進去。

     public final Message obtainMessage()

               {

                 return Message.obtain(this);

       }

這種工作方式如所示:

Message在裡面是一個鏈表的結構。在這個方法中,會首先去MessagePool(訊息資源回收筒)去找Message,如果有被回收的Message,則會將這個Message取出來進行再利用。如果沒有被回收的,這時系統才會真正new一個Message對象出來當然MessagePool不是無限大的,它最多隻能儲存十條回收的訊息,多出來的就直接刪除了。

至此,我們看到,一個Message經由Handler建立、發送,MessageQueue的入隊,Looper的抽取,又再一次地回到Handler進行處理。而繞的這一圈,也正好協助我們將同步操作變成了非同步作業。

在主線程(UI線程)裡,如果建立Handler時不傳入Looper對象,那麼將直接使用主線程(UI線程)的Looper對象(系統已經幫我們建立了);在其它線程裡,如果建立Handler時不傳入Looper對象,那麼,這個Handler將不能接收處理訊息。在建立Handler之前,為該線程準備好一個Looper(Looper.prepare),然後讓這個Looper跑起來(Looper.loop),抽取Message,這樣,Handler才能正常工作。

因此,Handler處理訊息總是在建立Handler的線程裡運行。

下面根據上述理論知識,來舉幾個例子:

例1:

       在UI線程中調用Handler的Post方法。代碼如下:

/* *  Android開發之訊息處理機制(二)——訊息迴圈 *  MessageCircle.java   *  Created on: 2011-9-2 *  Author: blueeagle *  Email: liujiaxiang@gmail.com */package blueeagle.com;import android.app.Activity;import android.os.Bundle;import android.os.Handler;public class MessageCircle extends Activity {    /** Called when the activity is first created. */private Handler myHandler = new Handler();private Runnable myRunnable = new Runnable(){@Overridepublic void run(){try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}System.out.println("Current Runnable Thread ID:"+Thread.currentThread().getId());}};    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);                System.out.println("Current Activity Thread ID:"+Thread.currentThread().getId());        myHandler.post(myRunnable);    }    }

 

程式運行結果如:

在這個程式中,我們New了一個沒有參數的Handler對象myHandler,這個myHandler自動與當前運行程式相關聯,也就是說,這個myHandler將與當前啟動並執行線程使用同一個訊息佇列,並且可以處理該隊列中的訊息。

在程式碼中,這個myHandler向訊息佇列中post了一個myRunnable對象。在myRunnable的run方法中,列印當前線程的ID。由結果得知,我們New出來的這個myHandler是把訊息或者Runnable對象送到當前的線程中的。這裡我們沒有調用線程的start()函數,因此也就不會去建立一個新的線程。而是在原有線程的內部,直接調用run()方法。因此輸出的ID是相同的。

那麼如何建立新的線程呢?在Android開發之訊息處理機制(一)中已經知道如何去建立一個新的線程,這裡繼續舉一個例子來進行比較說明。

例2:

首先需要建立一個線程,這個線程可以是自己建立的線程,也可以是HandlerThread,這個線程需要繼承於Thread類或者實現Runnable介面。其次需要有一個Handler,這個Handler可以是Handler也可以繼承於Handler並且需要有一個有Looper參數的建構函式。然後就可以進行訊息的分發執行了。

具體範例程式碼如下:

/* *  Android開發之訊息處理機制(二)——訊息迴圈 *  MessageCircle.java   *  Created on: 2011-9-2 *  Author: blueeagle *  Email: liujiaxiang@gmail.com */package blueeagle.com;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.HandlerThread;import android.os.Looper;import android.os.Message;import android.widget.TextView;public class MessageCircle extends Activity {    public class MyOwnThread extends Thread{    int sleepSpan = 2000;//休眠時間    boolean flag;// 線程執行標誌位    //----------------------------------//    //構造方法,初始化類的主要成員變數    public MyOwnThread(){    this.flag = true;    }    //----------------------------------//    //方法,線程執行方法    @Override    public void run(){    TextView myTextView = (TextView)findViewById(R.id.mytextview);    Looper.prepare();    int i = 1;    while(flag){    try{    Thread.sleep(sleepSpan);//休眠一段時間        System.out.println("Current MyOwnThread Thread ID: "+Thread.currentThread().getId());        myTextView.setText(i);        i++;    }    catch(Exception e){    e.printStackTrace();//捕獲異常並列印    }    }        Looper.loop();    }}public class MyHandler extends Handler{    public MyHandler(){}    public MyHandler(Looper looper){    super(looper);    }    @Override    public void handleMessage(Message msg){    System.out.println("Current MyHandle Thread ID: "+Thread.currentThread().getId());    TextView myTextView = (TextView)findViewById(R.id.mytextview);       int i = 1;    while(true){    try{    Thread.sleep(1000);//休眠一段時間        System.out.println("Current MyHandle Thread ID: "+Thread.currentThread().getId());        myTextView.setText("i");        i++;    }    catch(Exception e){    e.printStackTrace();//捕獲異常並列印    }    }        }}/** Called when the activity is first created. */private MyHandler myHandler = null;    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);                System.out.println("Current Activity Thread ID:"+Thread.currentThread().getId());        //myHandler.post(myRunnable);        HandlerThread myThread = new HandlerThread("myThread");//產生一個HandlerThread對象,使用Looper來處理訊息佇列。        myThread.start();//啟動這個線程        myHandler = new MyHandler(myThread.getLooper());//將一個線程綁定到myHandler這個對象上。        Message msg = myHandler.obtainMessage();//從myHandler這個對象中擷取訊息        msg.sendToTarget();//將msg對象發送給目標的Handler                //下面製造一個自己的線程        MyOwnThread myOwnThread = new MyOwnThread();        myOwnThread.start();    }    }

 

關於線程更新UI的方法,在(一)中已經說過。下面利用一個樣本將訊息迴圈過程進行展示,進而實現音樂播放,UI線程中更新歌詞的例子來說明訊息處理機制。例子較簡單,沒有實現歌詞同步等內容。只是為了更深刻的理解線程運行情況。

按照上述理論:代碼如下:

/* *  Android開發之訊息處理機制(二)——訊息迴圈 *  MusicPlayer.java   *  Created on: 2011-9-3 *  Author: blueeagle *  Email: liujiaxiang@gmail.com */package blueeagle.com;import android.app.Activity;import android.media.MediaPlayer;import android.os.Bundle;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.TextView;public class MusicPlayer extends Activity {    /** Called when the activity is first created. */Button myButton;TextView myTextView;private UIUpdateThread myUpdateThread = new UIUpdateThread(); //定義一個自己的UI更新的線程類MyHandler mHandler = new MyHandler();//定義自己的一個Handler類@Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        myButton = (Button)findViewById(R.id.myButton);        myTextView = (TextView)findViewById(R.id.myTextView);        myButton.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View v) {// TODO Auto-generated method stub                 System.out.println("主線程運行ID:"+Thread.currentThread().getId());new Thread(myUpdateThread).start();//起一個自己定義的線程}                });    }           class MyHandler extends Handler{//繼承Handler類時,必須重寫handleMessage方法 public MyHandler(){}public MyHandler(Looper l){super(l);}@Overridepublic void handleMessage(Message msg) {//執行接收到的通知,此時執行的順序是按照隊列進行,即先進先出 myTextView.setText(msg.toString());System.out.println("Current Handler Thread ID:"+Thread.currentThread().getId());} }//該線程將會在單獨的線程中運行class UIUpdateThread implements Runnable {  int i=1;        public void run() {              while (i<11) {                  Message msg = mHandler.obtainMessage();                mHandler.sendMessage(msg);                System.out.println("新線程運行ID:"+Thread.currentThread().getId());i++;                try {                      Thread.sleep(4000);                  } catch (InterruptedException e) {                      // TODO Auto-generated catch block                       e.printStackTrace();                  }              }          }      }}

關於簡單的音樂播放的源碼如下:

/* *  Android開發之訊息處理機制(二)——訊息迴圈 *  MusicPlayer.java   *  Created on: 2011-9-3 *  Author: blueeagle *  Email: liujiaxiang@gmail.com */package blueeagle.com;import android.app.Activity;import android.media.MediaPlayer;import android.os.Bundle;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.TextView;public class MusicPlayer extends Activity {    /** Called when the activity is first created. */private MediaPlayer mp;Button myButton;TextView myTextView;private UIUpdateThread myUpdateThread = new UIUpdateThread(); //定義一個自己的UI更新的線程類MyHandler mHandler = new MyHandler();//定義自己的一個Handler類@Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        myButton = (Button)findViewById(R.id.myButton);        myTextView = (TextView)findViewById(R.id.myTextView);        myButton.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View v) {// TODO Auto-generated method stubmp = MediaPlayer.create(MusicPlayer.this, R.raw.yinweiaiqing);mp.start();                System.out.println("主線程運行ID:"+Thread.currentThread().getId());new Thread(myUpdateThread).start();}                });    }           class MyHandler extends Handler{//繼承Handler類時,必須重寫handleMessage方法 public MyHandler(){}public MyHandler(Looper l){super(l);}@Overridepublic void handleMessage(Message msg) {//執行接收到的通知,此時執行的順序是按照隊列進行,即先進先出 myTextView.setText(msg.obj.toString());System.out.println("Current Handler Thread ID:"+Thread.currentThread().getId());} }//該線程將會在單獨的線程中運行    class UIUpdateThread implements Runnable {      int i = 0;    String myString[] = {    "這是一個即時播放的線程操作... ...",    "[00:08.79]《因為愛情》",    "[00:19.46]E 給你一張過去的CD",    "[00:28.68]聽聽那時我們的愛情",    "[00:34.12]有時會突然忘了",    "[00:37.48]我還在愛著你",    "[00:44.98]F 再唱不出那樣的歌曲",    "[00:50.48]聽到都會紅著臉躲避",    "[00:55.83]雖然會經常忘了",    "[00:59.33]我依然愛著你",    "[01:05.49]F 因為愛情 不會輕易悲傷",    "[01:05.49]F 因為愛情 不會輕易悲傷",    "[01:12.09]E 所以一切都是幸福的模樣",    "[01:17.24]F 因為愛情 簡單的生長",    "[01:22.24]E 依然隨時可以為你瘋狂",    "[01:27.21]F 因為愛情 怎麼會有滄桑",    "[01:34.30]E 所以我們還是年輕的模樣",    "[01:38.90]F 因為愛情 在那個地方",    "[01:44.32]E 依然還有人在那裡遊盪",    "[01:48.91]E&F 人來人往",    "[02:11.57]F 再唱不出那樣的歌曲",    "[02:17.70]聽到都會紅著臉躲避",    "[02:23.14]雖然會經常忘了",    "[02:26.26]E&F 我依然愛著你",    "[02:32.60]F 因為愛情 不會輕易悲傷",    "[02:39.22]E 所以一切都是幸福的模樣",    "[02:44.98]F 因為愛情 簡單的生長",    "[02:49.36]E 依然隨時可以為你瘋狂",    "[02:54.38]F 因為愛情 怎麼會有滄桑",    "[03:00.94]E 所以我們還是年輕的模樣",    "[03:06.04]F 因為愛情 在那個地方",    "[03:11.63]E 依然還有人在那裡遊盪",    "[03:17.04]E&F 人來人往",    "[03:21.98]E 給你一張過去的CD",    "[03:28.58]聽聽那時我們的愛情",    "[03:33.48]F 有時會突然忘了",    "[03:36.94]E&F 我還在愛著你"};        public void run() {              while (mp.isPlaying()) {  Message msg = mHandler.obtainMessage(1, 1, 1, myString[i]);                mHandler.sendMessage(msg);                System.out.println("新線程運行ID:"+Thread.currentThread().getId());                try {                      Thread.sleep(4000);                      i++;                if(i == myString.length){                i=myString.length-1;                }                 } catch (InterruptedException e) {                      // TODO Auto-generated catch block                       e.printStackTrace();                  }              }          }      }}

綜上,基本瞭解了Android下的關於線程,訊息迴圈的一些概念,還需要深入瞭解Looper如何運用在程式中等。

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.