1. 程式人生 > >Android Looper 架構 理解

Android Looper 架構 理解

在Android下面也有多執行緒的概念,在C/C++中,子執行緒可以是一個函式, 一般都是一個帶有迴圈的函式,來處理某些資料,優先執行緒只是一個複雜的運算過程,所以可能不需要while迴圈,運算完成,函式結束,執行緒就銷燬。對於那 些需要控制的執行緒,一般我們都是和互斥鎖相互關聯,從而來控制執行緒的進度,一般我們建立子執行緒,一種執行緒是很常見的,那就是帶有訊息迴圈的執行緒。
訊息迴圈是一個很有用的執行緒方式,曾經自己用C在Linux下面實現一個訊息迴圈的機制,往訊息佇列裡新增資料,然後非同步的等待訊息的返回。當訊息佇列為空的時候就會掛起執行緒,等待新的訊息的加入。這是一個很通用的機制。
在Android,這裡的執行緒分為有訊息迴圈的執行緒和沒有訊息迴圈的執行緒,有訊息迴圈的執行緒一般都會有一個Looper,這個事android的新 概念。我們的主執行緒(UI執行緒)就是一個訊息迴圈的執行緒。針對這種訊息迴圈的機制,我們引入一個新的機制Handle,我們有訊息迴圈,就要往訊息迴圈裡 面傳送相應的訊息,自定義訊息一般都會有自己對應的處理,訊息的傳送和清除,訊息的的處理,把這些都封裝在Handle裡面,注意Handle只是針對那 些有Looper的執行緒,不管是UI執行緒還是子執行緒,只要你有Looper,我就可以往你的訊息佇列裡面新增東西,並做相應的處理。
但是這裡還有一點,就是隻要是關於UI相關的東西,就不能放在子執行緒中,因為子執行緒是不能操作UI的,只能進行資料、系統等其他非UI的操作。
那麼什麼情況下面我們的子執行緒才能看做是一個有Looper的執行緒呢?我們如何得到它Looper的控制代碼呢?
Looper.myLooper();獲得當前的Looper
Looper.getMainLooper () 獲得UI執行緒的Lopper
我們看看Handle的初始化函式,如果沒有引數,那麼他就預設使用的是當前的Looper,如果有Looper引數,就是用對應的執行緒的Looper。
如果一個執行緒中呼叫Looper.prepare(),那麼系統就會自動的為該執行緒建立一個訊息佇列,然後呼叫 Looper.loop();之後就進入了訊息迴圈,這個之後就可以發訊息、取訊息、和處理訊息。這個如何傳送訊息和如何處理訊息可以再其他的執行緒中通過 Handle來做,但前提是我們的Hanle知道這個子執行緒的Looper,但是你如果不是在子執行緒執行 Looper.myLooper(),一般是得不到子執行緒的looper的。

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

所以很多人都是這樣做的:我直接在子執行緒中新建handle,然後在子執行緒中傳送訊息,這樣的話就失去了我們多執行緒的意義了。

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); } }

可以讓其他的執行緒來控制我們的handle,可以把 private EHandler mHandler ;放在外面,這樣我們的發訊息和處理訊息都可以在外面來定義,這樣增加程式程式碼的美觀,結構更加清晰。

對如任何的Handle,裡面必須要過載一個函式
public void handleMessage(Message msg)
這個函式就是我們的訊息處理,如何處理,這裡完全取決於你,然後通過 obtainMessage和 sendMessage等來生成和傳送訊息, removeMessages(0)來清除訊息佇列。Google真是太智慧了,這種框架的產生,我們寫程式碼更加輕鬆了。

有的時候,我們的子執行緒想去改變UI了,這個時候千萬不要再子執行緒中去修改,獲得UI執行緒的Looper,然後傳送訊息即可。

我們來看看高煥堂的程式碼:

// 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的時候不要看高煥堂的書,感覺他也不是很懂,倒還把我搞糊塗了。講了那麼多,完全是他自己的理解,他自己的理解很是複雜,關鍵的是把簡單的問題複雜化,並且複雜之後的東西還是錯的。我們看看Goole Music App的原始碼。

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

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

很明顯這兩句,是構建了一個子執行緒。並且這個子執行緒還是Looper的子執行緒,這裡很牛逼的使用了 mAlbumArtWorker.getLooper()這個函式,因為我們知道,我們能夠得到子執行緒的Looper的途徑只有一個:就是在子執行緒中呼叫 Looper.myLooper (),並且這個函式還要在我們perpare之後呼叫才能得到正確的Looper,但是他這裡用了一個這樣的什麼東東 getLooper,不知道它是如何實現的?

這裡有一個大概的思路,我們在子執行緒的的prepare之後呼叫 myLooper ()這個方法,然後儲存在一個成員變數中,這個getLooper就返回這個東西,但是這裡會碰到多執行緒的一個很突出的問題,同步。我們在父執行緒中呼叫 mAlbumArtWorker.getLooper(),但是想要這個返回正確的looper就必須要求我們的子執行緒運行了prepare,但是這個東 西實在子執行緒執行的,我們如何保證呢?

我們看Google是如何實現的?

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(null, this, 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();
        }
    }

我們知道,一個執行緒類的建構函式是在主執行緒中完成的,所以在我們的 Worker的建構函式中我們創佳一個執行緒,然後讓這個執行緒執行,這一這個執行緒的建立是指定一個 Runnabl,這裡就是我們的Worker本身,在主執行緒呼叫 t.start();,這後,我們子執行緒已經建立,並且開始執行work的run方法。然後下面的程式碼很藝術:

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

我們開始等待我們的子執行緒給mLooper賦值,如果不賦值我們就繼續等,然後我們的子執行緒在執行run方法之後,在給 mLooper賦值之後,通知worker夠著函式中的wait,然後我們的建構函式才能完成,所以我們說:
mAlbumArtWorker = new Worker(“album art worker”);
這句本身就是阻塞的,它建立了一個子執行緒,開啟了子執行緒,並且等待子執行緒給mLooper賦值,賦值完成之後,這個函式才返回,這樣才能保證我們的子執行緒的Looper的獲取絕對是正確的,這個構思很有創意。值得借鑑。