Calvin's Tech Space

          成于堅忍,毀于浮躁

             :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
          轉(zhuǎn)載地址:http://blog.csdn.net/jinlking/archive/2009/07/09/4333456.aspx

          在Android下面也有多線程的概念,在C/C++中,子線程可以是一個函數(shù),一般都是一個帶有循環(huán)的函數(shù),來處理某些數(shù)據(jù),優(yōu)先線程只是一個復雜的運算過程,所以可能不需要while循環(huán),運算完成,函數(shù)結(jié)束,線程就銷毀。對于那些需要控制的線程,一般我們都是和互斥鎖相互關(guān)聯(lián),從而來控制線程 的進度,一般我們創(chuàng)建子線程,一種線程是很常見的,那就是帶有消息循環(huán)的線程。

          消息循環(huán)是一個很有用的線程方式,曾經(jīng)自己用C在Linux下面實現(xiàn)一個消息循環(huán)的機制,往消息隊列里添加數(shù)據(jù),然后異步的等待消息的返回。當消息隊列為空的時候就會掛起線程,等待新的消息的加入。這是一個很通用的機制。

          在Android,這里的線程分為有消息循環(huán)的線程和沒有消息循環(huán)的線程,有消息循環(huán)的線程一般都會有一個Looper,這個是android的新概念。我們的主線程(UI線程)就是一個消息循環(huán)的線程。針對這種消息循環(huán)的機制,我們引入一個新的機制Handler,我們有消息循環(huán),就要往消息循環(huán)里面發(fā)送相應(yīng)的消息,自定義消息一般都會有自己對應(yīng)的處理,消息的發(fā)送和清除,消息的的處理,把這些都封裝在Handle里面,注意Handle只是針對那 些有Looper的線程,不管是UI線程還是子線程,只要你有Looper,我就可以往你的消息隊列里面添加東西,并做相應(yīng)的處理。

          但是這里還有一點,就是只要是關(guān)于UI相關(guān)的東西,就不能放在子線程中,因為子線程是不能操作UI的,只能進行數(shù)據(jù)、系統(tǒng)等其他非UI的操作。

          那么什么情況下面我們的子線程才能看做是一個有Looper的線程呢?我們?nèi)绾蔚玫剿麹ooper的句柄呢?

          Looper.myLooper();獲得當前的Looper

          Looper.getMainLooper () 獲得UI線程的Lopper

          我們看看Handle的初始化函數(shù),如果沒有參數(shù),那么他就默認使用的是當前的Looper,如果有Looper參數(shù),就是用對應(yīng)的線程的Looper。

          如果一個線程中調(diào)用Looper.prepare(),那么系統(tǒng)就會自動的為該線程建立一個消息隊列,然后調(diào)用 Looper.loop();之后就進入了消息循環(huán),這個之后就可以發(fā)消息、取消息、和處理消息。這個如何發(fā)送消息和如何處理消息可以在其他的線程中通過 Handle來做,但前提是我們的Handle知道這個子線程的Looper,但是你如果不是在子線程運行 Looper.myLooper(),一般是得不到子線程的looper的。

          public void run() {
                      synchronized (mLock) {
                          Looper.prepare();
                         //do something
                      }
                      Looper.loop();
                  }

          所以很多人都是這樣做的:我直接在子線程中新建handle,然后在子線程中發(fā)送消息,這樣的話就失去了我們多線程的意義了。
           1 class myThread extends Thread{
           2 
           3              private EHandler mHandler ;
           4 
           5              public void run() {
           6 
           7                  Looper myLooper, mainLooper;
           8 
           9                  myLooper = Looper.myLooper ();
          10 
          11                 mainLooper = Looper.getMainLooper ();
          12 
          13                 String obj;
          14 
          15                 if (myLooper == null ){
          16 
          17                          mHandler = new EHandler(mainLooper);
          18 
          19                          obj = "current thread has no looper!" ;
          20 
          21                 }
          22 
          23                 else {
          24 
          25                      mHandler = new EHandler(myLooper);
          26 
          27                      obj = "This is from current thread." ;
          28 
          29                 }
          30 
          31                 mHandler .removeMessages(0);
          32 
          33                 Message m = mHandler .obtainMessage(111, obj);
          34 
          35                 mHandler .sendMessage(m);
          36 
          37              }
          38 
          39   }

          可以讓其他的線程來控制我們的handle,可以把 private EHandler mHandler ;放在外面,這樣我們的發(fā)消息和處理消息都可以在外面來定義,這樣增加程序代碼的美觀,結(jié)構(gòu)更加清晰。

          對如任何的Handle,里面必須要重載一個函數(shù)

          public void handleMessage(Message msg)

          這個函數(shù)就是我們的消息處理,如何處理,這里完全取決于你,然后通過 obtainMessage和 sendMessage等來生成和發(fā)送消息, removeMessages(0)來清除消息隊列。Google真是太智慧了,這種框架的產(chǎn)生,我們寫代碼更加輕松了。

          有的時候,我們的子線程想去改變UI了,這個時候千萬不要再子線程中去修改,獲得UI線程的Looper,然后發(fā)送消息即可。

          我們來看看高煥堂的代碼:

          // class ac01 extends Activity {

                    // ………

                        public void onClick(View v) {

                               switch (v.getId()){

                               case 101:

                                            t = new myThread();

                                      t .start();

                                    break ;

                               case 102:

                            finish();

                                          break ;

                               }

                     }

          //------------------------------------------------------            

          class EHandler extends Handler {

                             public EHandler(Looper looper) {

                                 super (looper);

                             }

                             @Override

                             public void handleMessage(Message msg) {

                                tv .setText((String)msg. obj );

                         }

                     }

          //------------------------------------------------------            

          class myThread extends Thread{

                       private EHandler mHandler ;

                       public void run() {

                          Looper myLooper, mainLooper;

                          myLooper = Looper.myLooper ();

                          mainLooper = Looper.getMainLooper ();

                          String obj;

                          if (myLooper == null ){

                                  mHandler = new EHandler(mainLooper);

                                  obj = "current thread has no looper!" ;

                          }

                          else {

                               mHandler = new EHandler(myLooper);

                               obj = "This is from current thread." ;

                          }

                          mHandler .removeMessages(0);

                          Message m = mHandler .obtainMessage(1, 1, 1, obj);

                          mHandler .sendMessage(m);

                       }

            }

          }

          完全是不知所云,一坨狗屎。我們來看,在上面的run里面

          Looper myLooper, mainLooper;

          myLooper = Looper.myLooper (); //很明顯這個會返回空,因為你還沒有 prepare,不會返回Looper。

          mainLooper = Looper.getMainLooper ();

          建議大家在看Looper的時候不要看高煥堂的書,感覺他也不是很懂,倒還把我搞糊涂了。講了那么多,完全是他自己的理解,他自己的理解很是復雜,關(guān)鍵的是把簡單的問題復雜化,并且復雜之后的東西還是錯的。我們看看Goole Music App的源代碼。

          在MediaPlaybackActivity.java中,我們可以看一下再OnCreate中的有這樣的兩句:

                  mAlbumArtWorker = new Worker("album art worker");
                  mAlbumArtHandler = new AlbumArtHandler(mAlbumArtWorker.getLooper());

          很明顯這兩句,是構(gòu)建了一個子線程。并且這個子線程還是Looper的子線程,這里很牛逼的使用了 mAlbumArtWorker.getLooper()這個函數(shù),因為我們知道,我們能夠得到子線程的Looper的途徑只有一個:就是在子線程中調(diào)用 Looper.myLooper (),并且這個函數(shù)還要在我們perpare之后調(diào)用才能得到正確的Looper,但是他這里用了一個這樣的什么東東 getLooper,不知道它是如何實現(xiàn)的?

          這里有一個大概的思路,我們在子線程的的prepare之后調(diào)用 myLooper ()這個方法,然后保存在一個成員變量中,這個getLooper就返回這個東西,但是這里會碰到多線程的一個很突出的問題,同步。我們在父線程中調(diào)用 mAlbumArtWorker.getLooper(),但是想要這個返回正確的looper就必須要求我們的子線程運行了prepare,但是這個東 西實在子線程運行的,我們?nèi)绾伪WC呢?
           
          我們看Google是如何實現(xiàn)的?

           
            private class Worker implements Runnable {
                  
          private final Object mLock = new Object();
                  
          private Looper mLooper;
                 
                  
          /**
                   * Creates a worker thread with the given name. The thread
                   * then runs a {
          @link android.os.Looper}.
                   * 
          @param name A name for the new thread

                   
          */
                  Worker(String name) {
                      Thread t 
          = new Thread(nullthis, name);
                      t.setPriority(Thread.MIN_PRIORITY);
                      t.start();
                      
          synchronized (mLock) {
                          
          while (mLooper == null) {
                              
          try {
                                  mLock.wait();
                              } 
          catch (InterruptedException ex) {
                              }
                          }
                      }
                  }
                 
                  
          public Looper getLooper() {
                      
          return mLooper;
                  }
                 
                  
          public void run() {
                      
          synchronized (mLock) {
                          Looper.prepare();
                          mLooper 
          = Looper.myLooper();
                          mLock.notifyAll();
                      }
                      Looper.loop();
                  }
                 
                  
          public void quit() {
                      mLooper.quit();
                  }
              }

          我們知道,一個線程類的構(gòu)造函數(shù)是在主線程中完成的,所以在我們的 Worker的構(gòu)造函數(shù)中我們創(chuàng)佳一個線程,然后讓這個線程運行,這一這個線程的創(chuàng)建是指定一個 Runnabl,這里就是我們的Worker本身,在主線程調(diào)用 t.start();,這后,我們子線程已經(jīng)創(chuàng)建,并且開始執(zhí)行work的run方法。然后下面的代碼很藝術(shù):

          synchronized (mLock) {
                          while (mLooper == null) {
                              try {
                                  mLock.wait();
                              } catch (InterruptedException ex) {
                              }
                          }
                      }

          我們開始等待我們的子線程給mLooper賦值,如果不賦值我們就繼續(xù)等,然后我們的子線程在運行run方法之后,在給 mLooper賦值之后,通知worker夠著函數(shù)中的wait,然后我們的構(gòu)造函數(shù)才能完成,所以我們說:

          mAlbumArtWorker = new Worker("album art worker");

          這句本身就是阻塞的,它創(chuàng)建了一個子線程,開啟了子線程,并且等待子線程給mLooper賦值,賦值完成之后,這個函數(shù)才返回,這樣才能保證我們的子線程的Looper的獲取絕對是正確的,這個構(gòu)思很有創(chuàng)意。值得借鑒。

          PS:
          1、發(fā)送消息并不會阻塞線程,而處理消息時會阻塞線程,這是因為Handler 處理完一個 Message 對象才會接著去取下面一個消息進行處理。
          Handler對于Message的處理不是并發(fā)的。一個Looper 只有處理完一條Message才會讀取下一條,所以消息的處理是阻塞形式的,如果在handler里做耗時的操作,則調(diào)用者線程會阻塞。但是如果用不同的Looper則能達到并發(fā)的目的。(http://windywindy.javaeye.com/blog/464185

          2、Android提供了一個HandlerThread來實現(xiàn)并發(fā)處理message的目的,HandlerThread在實現(xiàn)上與Music應(yīng)用的Worker類相似。

          參考:
          posted on 2010-11-29 14:55 calvin 閱讀(3215) 評論(0)  編輯  收藏 所屬分類: Android
          主站蜘蛛池模板: 永吉县| 荔波县| 佛学| 南和县| 辽中县| 靖州| 庐江县| 龙胜| 咸阳市| 崇州市| 天全县| 平南县| 巫溪县| 罗甸县| 开鲁县| 五家渠市| 本溪市| 宾川县| 兴宁市| 桦川县| 彰化市| 寿宁县| 扎鲁特旗| 乌兰察布市| 武夷山市| 肃宁县| 梁平县| 墨玉县| 五常市| 涟源市| 乌鲁木齐县| 白沙| 丰原市| 海门市| 婺源县| 铁岭市| 乳山市| 方城县| 西藏| 肥乡县| 包头市|