如何使用Handler

在Android使用執行緒要非常的小心, 使用者在進行操作時, 執行緒也在進行大量運算, 會造成使用者畫面卡死不動, 這樣的使用者體驗是不好的。

Android將Main Thread用來處理UI, 因此需要使用Thread讓大量運算在背景跑, 卻不影響使用者操作的畫面, 而如果需要畫面更新, 則會透過Handler機制去更新。

執行緒處理訊息機制(Handler、Looper、Message and MessageQueue)

/*
* 一個Thread只能有一個Looper。
* 當Message處理完畢後, 會將Message發送給Handler。
*/
android.os.Looper

/*
* 一個Thread可以有多個Handler。
* 負責將Message送往MessageQueue, 並且接收Looper丟出來的Message。
*/
android.os.Handler

/*
* 一個Thread只能有一個MessageQueue。
* 負責裝載Message的佇列, 對Message進行管理, 是一個無限制的鏈結串列。
*/
android.os.MessageQueue

//執行緒上要處理的訊息。

如上圖, Handler負責派送訊息, 交給MessageQueue進行排隊, 再透過Looper將每一個Message Object丟給Handler處理。

也許上面這些東西會有點陌生, 是因為Android Main Thread 一開始就先幫你綁定好了, 你不需要自訂初始化Looper並且綁定Handler, 下面的做法都是開啟Thread運算完後, 去呼叫Main Thread進行畫面更新。

new Thread(new Runnable() {
    public void run() {
        //這邊是背景thread在運作, 這邊可以處理比較長時間或大量的運算

        ((Activity) mContext).runOnUiThread(new Runnable() {
            public void run() {
                //這邊是呼叫main thread handler幫我們處理UI部分                

            }
        });
    }
}).start();
//或者


view.post(new Runnable(){
    public void run(){
        //更新畫面

    }
});

//又或者另外一種寫法

private Handler mHandler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        switch(msg.what){
            case 1:
                //處理少量資訊或UI

            break;
        }
    }
};

new Thread(new Runnable(){
    public void run(){
        Message msg = new Message();
        msg.what = 1;
        mHandler.sendMessage(msg);
    }
});

第一種寫法是實作Runnable介面讓Main Thread的Handler進行callback(請參考android的消息處理機制) 一般是用來處理大量數據或者長時間運算, 最後再利用Activity內建的方法runOnUiThread呼叫main thread handler幫忙處理UI部分。 第二種寫法是自己定義一個Message物件透過Hanlder去進行處理。

你也可以拿Main Thread的Handler來處理比較少量資料。

new Handler(mContext.getMainLooper()).post(new Runnable(){
   public void run(){
       //處理少量資訊或UI

   }
});

甚至你可以使用Delay的方法來延後Thread處理。

 new Handler().postDelayed(new Runnable(){
   public void run(){
       //處理少量資訊或UI

   }
}, 3000);

上面是在3秒後處理少量資訊。

以上都是利用MainThread上面的Looper進行處理, 實際上你也可以自己定義自己的Looper。

new Thread(new Runnable() {
    public void run() {
        Log.e(TAG, "!!<A");
        Looper.prepare();
        new Handler().post(new Runnable(){

            @Override
            public void run() {
                Log.e(TAG, "B1");
            }
        });
        new Handler().post(new Runnable(){

            @Override
            public void run() {
                Log.e(TAG, "B2");
                Looper.myLooper().quit();
            }
        });

        Looper.loop();
                
        Log.e(TAG, "C");
        ((Activity) mContext).runOnUiThread(new Runnable() {
            public void run() {
                Log.e(TAG, "D");
            }
        });
    }
}).start();

輸出為A、B1、B2、C、D 由上面的程式碼可以看到我們自己定義的Looper, 對於Thread內的Handler會變成跟自己定義的Looper進行綁定, 也就是說這邊是屬於Background Thread部分, 不行拿來更新UI, 而直到呼叫Looper內的quit, 則會將該Looper以及MessageQueue移除, Thread才會繼續往下執行, 注意: 假設這邊如果還有Message正在處理, 則會將該Message處理完畢, 再將後面未處理的Message全部移除。 如果要更新UI則會再透過Main Thread的handler去處理UI部分。 當然你也可以使用HandlerThread

講了那麼多訊息,訊息究竟是一個怎樣的物件?

android.os.Message

只要是透過Handler派送的訊息最後都會被包成Message, 送進MessageQueue屠宰等待派送, 每一個Message都會認得把自己送進來的Handler(記仇)。

Message m = Message.obtain(handler, runnable);
m.sendToTarget();

##Message有幾種參數

參數名稱    型別         用途
what       int         標記識別符號
obj        Object      物件, 必須Parcelable
data       Bundle      Bundle物件
callback   Runnable    實作Runnable的callback

以上是比較常用的, 還有一些請參考官網

##參考: Android 中的 MessageQueue 機制 android的消息處理機制


书籍推荐