[Handler]android-Handler解釋

大搜車-自娛發表於2014-12-01

Android開發過程中為什麼要多執行緒

我們建立的Service、Activity以及Broadcast均是一個主執行緒處理,這裡我們可以理解為UI執行緒。但是在操作一些耗時操作時,比如I/O讀寫的大檔案讀寫,資料庫操作以及網路下載需要很長時間,為了不阻塞使用者介面,出現ANR的響應提示視窗,這個時候我們可以考慮使用Thread執行緒來解決。

  Android中使用Thread執行緒會遇到哪些問題

對於從事過J2ME開發的程式設計師來說Thread比較簡單,直接匿名建立重寫run方法,呼叫start方法執行即可。或者從Runnable介面繼承,但對於Android平臺來說UI控制元件都沒有設計成為執行緒安全型別,所以需要引入一些同步的機制來使其重新整理,這點Google在設計Android時倒是參考了下Win32的訊息處理機制。

postInvalidate()方法

對於執行緒中的重新整理一個View為基類的介面,可以使用postInvalidate()方法線上程中來處理,其中還提供了一些重寫方法比如postInvalidate(int left,int top,int right,int bottom) 來重新整理一個矩形區域,以及延時執行,比如postInvalidateDelayed(long delayMilliseconds)postInvalidateDelayed(long delayMilliseconds,int left,int top,int right,int bottom) 方法,其中第一個引數為毫秒,如下:

void

postInvalidate()

void

postInvalidate(int left, int top, int right, int bottom)

void

postInvalidateDelayed(long delayMilliseconds)

void

postInvalidateDelayed(long delayMilliseconds, int left, int top, int right, int bottom)

Handler

當然推薦的方法是通過一個Handler來處理這些,可以在一個執行緒的run方法中呼叫handler物件的postMessagesendMessage方法來實現,Android程式內部維護著一個訊息佇列,會輪訓處理這些,如果你是Win32程式設計師可以很好理解這些訊息處理,不過相對於Android來說沒有提供PreTranslateMessage這些干涉內部的方法。

訊息的處理者,handler負責將需要傳遞的資訊封裝成Message,通過呼叫handler物件的obtainMessage()來實現。將訊息傳遞給Looper,這是通過handler物件的sendMessage()來實現的。繼而由LooperMessage放入MessageQueue中。Looper物件看到MessageQueue中含有Message,就將其廣播出去。該handler物件收到該訊息後,呼叫相應的handler物件的handleMessage()方法對其進行處理。

 

Handler主要接受子執行緒傳送的資料,並用此資料配合主執行緒更新UI.
      
當應用程式啟動時,Android首先會開啟一個主執行緒 (也就是UI執行緒) , 主執行緒為管理介面中的UI控制元件,進行事件分發,比如說,你要是點選一個 Button ,Android會分發事件到Button上,來響應你的操作。  如果此時需要一個耗時的操作,例如:聯網讀取資料, 或者讀取本地較大的一個檔案的時候,你不能把這些操作放在主執行緒中,,如果你放在主執行緒中的話,介面會出現假死現象,如果5秒鐘還沒有完成的話,,會收到Android系統的一個錯誤提示  "強制關閉".  這個時候我們需要把這些耗時的操作,放在一個子執行緒中,因為子執行緒涉及到UI更新,,Android主執行緒是執行緒不安全的,也就是說,更新UI只能在主執行緒中更新,子執行緒中操作是危險的.這個時候,Handler就出現了,來解決這個複雜的問題由於Handler執行在主執行緒中(UI執行緒中),  它與子執行緒可以通過Message物件來傳遞資料,這個時候,Handler就承擔著接受子執行緒傳過來的(子執行緒用sedMessage()方法傳弟)Message物件,(裡面包含資料)  ,把這些訊息放入主執行緒佇列中,配合主執行緒進行更新UI


Handler
一些特點:handler可以分發Message物件和Runnable物件到主執行緒中,每個Handler例項,都會繫結到建立他的執行緒中(一般是位於主執行緒),
      
它有兩個作用: (1)安排訊息或Runnable在某個主執行緒中某個地方執行

                           (2)安排一個動作在不同的執行緒中執行
        Handler
中分發訊息的一些方法
        post(Runnable)
        postAtTime(Runnable,long)
        postDelayed(Runnable long)
        sendEmptyMessage(int)
        sendMessage(Message)
        sendMessageAtTime(Message,long)
        sendMessageDelayed(Message,long)
      
以上post類方法允許你排列一個Runnable物件到主執行緒佇列中,sendMessage類方法,允許你安排一個帶資料的Message物件到佇列中,等待更新.

Handler例項
    // 
子類需要繼承Hendler類,並重寫handleMessage(Message msg) 方法,用於接受執行緒資料
     // 
以下為一個例項,它實現的功能 :通過執行緒修改介面Button的內容

public class MyHandlerActivity extends Activity {

    Button button;

    MyHandler myHandler;

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentview(R.layout.handlertest);

        button = (Button) findViewById(R.id.button);

        myHandler = new MyHandler();

        //當建立一個新的Handler例項時,它會繫結到當前執行緒和訊息的佇列中,開始分發資料

        // Handler有兩個作用, (1) :定時執行MessageRunnalbe物件

        // (2):讓一個動作,在不同的執行緒中執行.

        //它安排訊息,用以下方法

        // post(Runnable)

        // postAtTime(Runnable,long)

        // postDelayed(Runnable,long)

        // sendEmptyMessage(int)

        // sendMessage(Message);

        // sendMessageAtTime(Message,long)

        // sendMessageDelayed(Message,long)

        //以上方法以 post開頭的允許你處理Runnable物件

        //sendMessage()允許你處理Message物件(Message裡可以包含資料,)

        MyThread m = new MyThread();

        new Thread(m).start();

    }

    /**

     *接受訊息,處理訊息 ,Handler會與當前主執行緒一塊執行

     * */

    class MyHandler extends Handler {

        public MyHandler() {

        }

        public MyHandler(Looper L) {

            super(L);

        }

        //子類必須重寫此方法,接受資料

        @Override

        public void handleMessage(Message msg) {

            // TODO Auto-generated method stub

            Log.d("MyHandler", "handleMessage......");

            super.handleMessage(msg);

            //此處可以更新UI

            Bundle b = msg.getData();

            String color = b.getString("color");

            MyHandlerActivity.this.button.append(color);

        }

    }

    class MyThread implements Runnable {

        public void run() {

            try {

                Thread.sleep(10000);

            } catch (InterruptedException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

            Log.d("thread.......", "mThread........");

            Message msg = new Message();

            Bundle b = new Bundle();//存放資料

            b.putString("color", "我的");

            msg.setData(b);

            MyHandlerActivity.this.myHandler.sendMessage(msg); //Handler傳送訊息,更新UI

        }

    }
}

  Looper

其實Android中每一個Thread都跟著一個LooperLooper可以幫助Thread維護一個訊息佇列,昨天的問題 Can't create handler inside thread 錯誤 一文中提到這一概念,但是LooperHandler沒有什麼關係,我們從開源的程式碼可以看到Android還提供了一個Thread繼承類HanderThread可以幫助我們處理,在HandlerThread物件中可以通過getLooper方法獲取一個Looper物件控制控制程式碼,我們可以將其這個Looper物件對映到一個Handler中去來實現一個執行緒同步機制,Looper物件的執行需要初始化Looper.prepare方法就是昨天我們看到的問題,同時推出時還要釋放資源,使用Looper.release方法。

LooperMessageQueue的管理者。每一個MessageQueue都不能脫離Looper而存在,Looper物件的建立是通過prepare函式來實現的。同時每一個Looper物件和一個執行緒關聯。通過呼叫Looper.myLooper()可以獲得當前執行緒的Looper物件 
建立一個Looper物件時,會同時建立一個MessageQueue物件。除了主執行緒有預設的Looper,其他執行緒預設是沒有MessageQueue物件的,所以,不能接受Message。如需要接受,自己定義一個Looper物件(通過prepare函式),這樣該執行緒就有了自己的Looper物件和MessageQueue資料結構了。 
Looper
MessageQueue中取出Message然後,交由HandlerhandleMessage進行處理。處理完成後,呼叫Message.recycle()將其放入Message Pool中。

Message

對於AndroidHandler可以傳遞一些內容,通過Bundle物件可以封裝StringInteger以及Blob二進位制物件,我們通過線上程中使用Handler物件的    sendEmptyMessagesendMessage方法來傳遞一個Bundle物件到Handler處理器。對於Handler類提供了重寫方法handleMessage(Message msg) 來判斷,通過msg.what來區分每條資訊。將Bundle解包來實現Handler類更新UI執行緒中的內容實現控制元件的重新整理操作。相關的Handler物件有關訊息傳送sendXXXX相關方法如下,同時還有postXXXX相關方法,這些和Win32中的道理基本一致,一個為傳送後直接返回,一個為處理後才返回。

Message:訊息物件,Message Queue中的存放的物件。一個Message Queue中包含多個Message Message例項物件的取得,通常使用Message類裡的靜態方法obtain(),該方法有多個過載版本可供選擇;它的建立並不一定是直接建立一個新的例項,而是先從Message Pool(訊息池)中看有沒有可用的Message例項,存在則直接取出返回這個例項。如果Message Pool中沒有可用的Message例項,則才用給定的引數建立一個Message物件。呼叫removeMessages()時,將MessageMessage Queue中刪除,同時放入到Message Pool中。除了上面這種方式,也可以通過Handler物件的obtainMessage()獲取一個Message例項。

final boolean

sendEmptyMessage(int what)

final boolean

sendEmptyMessageAtTime(int what, long uptimeMillis)

final boolean

sendEmptyMessageDelayed(int what, long delayMillis)

final boolean

sendMessage(Message msg)

final boolean

sendMessageAtFrontOfQueue(Message msg)

boolean

sendMessageAtTime(Message msg, long uptimeMillis)

final boolean

sendMessageDelayed(Message msg, long delayMillis)

MessageQueue

是一種資料結構,見名知義,就是一個訊息佇列,存放訊息的地方。每一個執行緒最多隻可以擁有一個MessageQueue資料結構。 
建立一個執行緒的時候,並不會自動建立其MessageQueue。通常使用一個Looper物件對該執行緒的MessageQueue進行管理。主執行緒建立時,會建立一個預設的Looper物件,而Looper物件的建立,將自動建立一個Message Queue。其他非主執行緒,不會自動建立Looper,要需要的時候,通過呼叫prepare函式來實現。
 
java.util.concurrent物件分析

對於過去從事Java開發的程式設計師不會對Concurrent物件感到陌生吧,他是JDK 1.5以後新增的重要特性作為掌上裝置,我們不提倡使用該類,考慮到Android為我們已經設計好的Task機制,我們這裡Android開發網對其不做過多的贅述。

Task以及AsyncTask

Android中還提供了一種有別於執行緒的處理方式,就是Task以及AsyncTask,從開原始碼中可以看到是針對Concurrent的封裝,開發人員可以方便的處理這些非同步任務。 當然涉及到同步機制的方法和技巧還有很多,考慮時間和篇幅問題不再做過多的描

相關文章