統(tǒng)計(jì)

          留言簿(1)

          DB

          Others

          QA

          Tech Website

          閱讀排行榜

          評(píng)論排行榜

          【轉(zhuǎn)】深入理解Android消息處理系統(tǒng)

               熟悉Windows編程的朋友可能知道Windows程序是消息驅(qū)動(dòng)的,并且有全局的消息循環(huán)系統(tǒng)。而Android應(yīng)用程序也是消息驅(qū)動(dòng)的,按道理來(lái)說(shuō)也應(yīng)該提供消息循環(huán)機(jī)制。實(shí)際上谷歌參考了Windows的消息循環(huán)機(jī)制,也在Android系統(tǒng)中實(shí)現(xiàn)了消息循環(huán)機(jī)制。Android通過(guò)Looper、Handler來(lái)實(shí)現(xiàn)消息循環(huán)機(jī)制,Android消息循環(huán)是針對(duì)線(xiàn)程的(每個(gè)線(xiàn)程都可以有自己的消息隊(duì)列和消息循環(huán))。本文深入介紹一下Android消息處理系統(tǒng)原理。

              Android系統(tǒng)中Looper負(fù)責(zé)管理線(xiàn)程的消息隊(duì)列和消息循環(huán),具體實(shí)現(xiàn)請(qǐng)參考Looper的源碼。 可以通過(guò)Loop.myLooper()得到當(dāng)前線(xiàn)程的Looper對(duì)象,通過(guò)Loop.getMainLooper()可以獲得當(dāng)前進(jìn)程的主線(xiàn)程的Looper對(duì)象。

              前面提到Android系統(tǒng)的消息隊(duì)列和消息循環(huán)都是針對(duì)具體線(xiàn)程的,一個(gè)線(xiàn)程可以存在(當(dāng)然也可以不存在)一個(gè)消息隊(duì)列和一個(gè)消息循環(huán)(Looper),特定線(xiàn)程的消息只能分發(fā)給本線(xiàn)程,不能進(jìn)行跨線(xiàn)程,跨進(jìn)程通訊。但是創(chuàng)建的工作線(xiàn)程默認(rèn)是沒(méi)有消息循環(huán)和消息隊(duì)列的,如果想讓該線(xiàn)程具有消息隊(duì)列和消息循環(huán),需要在線(xiàn)程中首先調(diào)用Looper.prepare()來(lái)創(chuàng)建消息隊(duì)列,然后調(diào)用Looper.loop()進(jìn)入消息循環(huán)。如下例所示:

            class LooperThread extends Thread {
                public Handler mHandler;
          
                public void run() {
                    Looper.prepare();
          
                    mHandler = new Handler() {
                        public void handleMessage(Message msg) {
                            // process incoming messages here
                        }
                    };
          
                    Looper.loop();
                }
            }

          這樣你的線(xiàn)程就具有了消息處理機(jī)制了,在Handler中進(jìn)行消息處理。

               Activity是一個(gè)UI線(xiàn)程,運(yùn)行于主線(xiàn)程中,Android系統(tǒng)在啟動(dòng)的時(shí)候會(huì)為Activity創(chuàng)建一個(gè)消息隊(duì)列和消息循環(huán)(Looper)。詳細(xì)實(shí)現(xiàn)請(qǐng)參考ActivityThread.java文件。

               Handler的作用是把消息加入特定的(Looper)消息隊(duì)列中,并分發(fā)和處理該消息隊(duì)列中的消息。構(gòu)造Handler的時(shí)候可以指定一個(gè)Looper對(duì)象,如果不指定則利用當(dāng)前線(xiàn)程的Looper創(chuàng)建。詳細(xì)實(shí)現(xiàn)請(qǐng)參考Looper的源碼。

               Activity、Looper、Handler的關(guān)系如下圖所示:

               一個(gè)Activity中可以創(chuàng)建多個(gè)工作線(xiàn)程或者其他的組件,如果這些線(xiàn)程或者組件把他們的消息放入Activity的主線(xiàn)程消息隊(duì)列,那么該消息就會(huì)在主線(xiàn)程中處理了。因?yàn)橹骶€(xiàn)程一般負(fù)責(zé)界面的更新操作,并且Android系統(tǒng)中的weget不是線(xiàn)程安全的,所以這種方式可以很好的實(shí)現(xiàn)Android界面更新。在Android系統(tǒng)中這種方式有著廣泛的運(yùn)用。

               那么另外一個(gè)線(xiàn)程怎樣把消息放入主線(xiàn)程的消息隊(duì)列呢?答案是通過(guò)Handle對(duì)象,只要Handler對(duì)象以主線(xiàn)程的Looper創(chuàng)建,那么調(diào)用Handler的sendMessage等接口,將會(huì)把消息放入隊(duì)列都將是放入主線(xiàn)程的消息隊(duì)列。并且將會(huì)在Handler主線(xiàn)程中調(diào)用該handler的handleMessage接口來(lái)處理消息。

               這里面涉及到線(xiàn)程同步問(wèn)題,請(qǐng)先參考如下例子來(lái)理解Handler對(duì)象的線(xiàn)程模型:

          1、首先創(chuàng)建MyHandler工程。

          2、在MyHandler.java中加入如下的代碼:

          package com.simon;
          
          import android.app.Activity;
          import android.os.Bundle;
          import android.os.Message;
          import android.util.Log;
          import android.os.Handler;
          
          public class MyHandler extends Activity {
          	static final String TAG = "Handler";
          	Handler h = new Handler(){
              	public void handleMessage (Message msg)
              	{
              		switch(msg.what)
              		{
              		case HANDLER_TEST:
              			Log.d(TAG, "The handler thread id = " + Thread.currentThread().getId() + "\n");
              			break;
              		}
              	}
              };
          
          	static final int HANDLER_TEST = 1;
              /** Called when the activity is first created. */
              @Override
              public void onCreate(Bundle savedInstanceState) {
                  super.onCreate(savedInstanceState);
                  Log.d(TAG, "The main thread id = " + Thread.currentThread().getId() + "\n");
          
                  new myThread().start();
                  setContentView(R.layout.main);
              }
          
              class myThread extends Thread
              {
              	public void run()
              	{
              		Message msg = new Message();
              		msg.what = HANDLER_TEST;
              		h.sendMessage(msg);
              		Log.d(TAG, "The worker thread id = " + Thread.currentThread().getId() + "\n");
              	}
              }
          }

          在這個(gè)例子中我們主要是打印,這種處理機(jī)制各個(gè)模塊的所處的線(xiàn)程情況。如下是我的機(jī)器運(yùn)行結(jié)果:

          09-10 23:40:51.478: DEBUG/Handler(302): The main thread id = 1
          09-10 23:40:51.569: DEBUG/Handler(302): The worker thread id = 8
          09-10 23:40:52.128: DEBUG/Handler(302): The handler thread id = 1

          我們可以看出消息處理是在主線(xiàn)程中處理的,在消息處理函數(shù)中可以安全的調(diào)用主線(xiàn)程中的任何資源,包括刷新界面。工作線(xiàn)程和主線(xiàn)程運(yùn)行在不同的線(xiàn)程中,所以必須要注意這兩個(gè)線(xiàn)程間的競(jìng)爭(zhēng)關(guān)系。

               上例中,你可能注意到在工作線(xiàn)程中訪(fǎng)問(wèn)了主線(xiàn)程handler對(duì)象,并在調(diào)用handler的對(duì)象向消息隊(duì)列加入了一個(gè)消息。這個(gè)過(guò)程中會(huì)不會(huì)出現(xiàn)消息隊(duì)列數(shù)據(jù)不一致問(wèn)題呢?答案是handler對(duì)象不會(huì)出問(wèn)題,因?yàn)閔andler對(duì)象管理的Looper對(duì)象是線(xiàn)程安全的,不管是加入消息到消息隊(duì)列和從隊(duì)列讀出消息都是有同步對(duì)象保護(hù)的,具體請(qǐng)參考Looper.java文件。上例中沒(méi)有修改handler對(duì)象,所以handler對(duì)象不可能會(huì)出現(xiàn)數(shù)據(jù)不一致的問(wèn)題。

               通過(guò)上面的分析,我們可以得出如下結(jié)論:

          1、如果通過(guò)工作線(xiàn)程刷新界面,推薦使用handler對(duì)象來(lái)實(shí)現(xiàn)。

          2、注意工作線(xiàn)程和主線(xiàn)程之間的競(jìng)爭(zhēng)關(guān)系。推薦handler對(duì)象在主線(xiàn)程中構(gòu)造完成(并且啟動(dòng)工作線(xiàn)程之后不要再修改之,否則會(huì)出現(xiàn)數(shù)據(jù)不一致),然后在工作線(xiàn)程中可以放心的調(diào)用發(fā)送消息SendMessage等接口。

          3、除了2所述的hanlder對(duì)象之外的任何主線(xiàn)程的成員變量如果在工作線(xiàn)程中調(diào)用,仔細(xì)考慮線(xiàn)程同步問(wèn)題。如果有必要需要加入同步對(duì)象保護(hù)該變量。

          4、handler對(duì)象的handleMessage接口將會(huì)在主線(xiàn)程中調(diào)用。在這個(gè)函數(shù)可以放心的調(diào)用主線(xiàn)程中任何變量和函數(shù),進(jìn)而完成更新UI的任務(wù)。

          5、Android很多API也利用Handler這種線(xiàn)程特性,作為一種回調(diào)函數(shù)的變種,來(lái)通知調(diào)用者。這樣Android框架就可以在其線(xiàn)程中將消息發(fā)送到調(diào)用者的線(xiàn)程消息隊(duì)列之中,不用擔(dān)心線(xiàn)程同步的問(wèn)題。

               深入理解Android消息處理機(jī)制對(duì)于應(yīng)用程序開(kāi)發(fā)非常重要,也可以讓你對(duì)線(xiàn)程同步有更加深刻的認(rèn)識(shí)。以上是最近Simon學(xué)習(xí)Android消息處理機(jī)制的一點(diǎn)兒總結(jié),如有錯(cuò)誤之處請(qǐng)不吝指教。

          參考資料:

          http://www.wscxy.com/nuaa/article.asp?id=116

          http://www.android1.net/Topic.aspx?BoardID=11&TopicID=631&Page=1

          http://www.android1.net/Topic.aspx?BoardID=11&TopicID=632

          http://www.android1.net/Topic.aspx?BoardID=11&TopicID=625

          posted on 2011-07-29 11:53 XXXXXX 閱讀(267) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): Android Dev

          主站蜘蛛池模板: 五大连池市| 泗水县| 阜宁县| 类乌齐县| 沙坪坝区| 房产| 江达县| 桃园县| 秦皇岛市| 明光市| 屯昌县| 郸城县| 昌黎县| 花莲市| 西贡区| 太谷县| 巴里| 通道| 余庆县| 文山县| 宜章县| 松滋市| 林甸县| 靖州| 铅山县| 安新县| 威远县| 曲周县| 方城县| 五大连池市| 福海县| 筠连县| 花莲县| 乌拉特前旗| 林西县| 大宁县| 香河县| 九江县| 分宜县| 安徽省| 临海市|