上篇 Android Looper + Handler + Message + MessageQueue(Worker Thread)介紹 Worker Thread (Android訊息傳送機制)
本篇會持續示範訊息傳送機制的其他進階用法,

1.傳送訊息夾帶參數

這是基本建立訊息並使用Handler傳送訊息的用法

    private void sendMessageToWorkerHandler()
    {
        Handler handler = mCustomizeWorkerThread.getHandler();
        if (handler != null) {
            Message message = handler.obtainMessage(2);
            handler.sendMessage(message);
        }
    }

傳送訊息可以夾帶參數,讓Handler在處理訊息(handleMessage方法)時,可根據參數作更多處理 e.g.,
夾帶簡單整數型態arg1和arg2

    private void sendMessageWithArgsToWorkerHandler()
    {
        Handler handler = mCustomizeWorkerThread.getHandler();
        if (handler != null) {
            Message message = Message.obtain();
            message.what = 3;
            message.arg1 = 10;
            message.arg2 = 100;
            handler.sendMessage(message);
        }
    }

另外還可以夾帶自定義類別的物件,以下為自定義類別,為了簡單示範,只持有一個String和一個Int類別成員

package com.foxx.threads;
public class CustomizeMessageObject
{
    private String mName = "";
    private int mType;
    public CustomizeMessageObject(String name, int type){
        mName = name;
        mType = type;
    }
    public String getName()
    {
        return mName;
    }
    public void setName(String name)
    {
        mName = name;
    }
    public int getType()
    {
        return mType;
    }
    public void setType(int type)
    {
        mType = type;
    }
}

夾帶自定義物件的方式

    private void sendMessageWithObjectToWorkerHandler()
    {
        Handler handler = mCustomizeWorkerThread.getHandler();
        if (handler != null) {
            Message message = Message.obtain();
            message.what = 4;
            message.obj = new CustomizeMessageObject("Foxx", 123);
            handler.sendMessage(message);
        }
    }

修改在Activity發送訊息的部份,點擊按鈕會依序發送3個訊息

    @Override
    public void onClick(View view)
    {
        int uiId = view.getId();
        switch (uiId) {
            case R.id.startThread:
                sendMessageToWorkerHandler();
                sendMessageWithArgsToWorkerHandler();
                sendMessageWithObjectToWorkerHandler();
                break;
        }
    }

修改CustomizeWorkerHandler處理訊息的部份,針對message.what的數值作判斷

    public void handleMessage(Message message)
    {
        switch (message.what) {
            case 2:
                String result = HttpPageData.getPageData("https://www.google.com.tw/");
                setResultToMainActivity(result);
                break;
            case 3:
                Log.d(TAG, "message.arg1:" + message.arg1);
                Log.d(TAG, "message.arg2:" + message.arg2);
                break;
            case 4:
                CustomizeMessageObject messageObject = (CustomizeMessageObject) message.obj;
                Log.d(TAG, "MessageObject.name:" + messageObject.getName());
                Log.d(TAG, "MessageObject.type:" + messageObject.getType());
                break;
        }
    }

以下是Logcat的輸出

06-12 11:34:01.463: D/WorkerHandler(16289): message.arg1:10
06-12 11:34:01.463: D/WorkerHandler(16289): message.arg2:100
06-12 11:34:01.463: D/WorkerHandler(16289): MessageObject.name:Foxx
06-12 11:34:01.463: D/WorkerHandler(16289): MessageObject.type:123

 

2.提醒使用者訊息處理

假設處理訊息時並沒有提醒使用者,會讓使用者誤判點擊按鍵卻沒有反應造成使用者多次點擊,為了避免這種情況,最好在送出訊息時出現提示。
提示可由Dialog或是ProgressDialog組成。
首先在MainActivity加入並建立提示元件,提供外部存取的方法

public class MainActivity extends ActionBarActivity implements OnClickListener
{
    ...
    private AlertDialog mProcessingMessageDialog;
    private void initUIComponents()
    {
        initProcessingMessageDialog();
        ...
    }
    private void initProcessingMessageDialog()
    {
        mProcessingMessageDialog = new AlertDialog.Builder(this).create();
        mProcessingMessageDialog.setTitle("Processing...");
        mProcessingMessageDialog.setCanceledOnTouchOutside(false);
    }
    public AlertDialog getProcessingMessageDialog(){
        return mProcessingMessageDialog;
    }

接著在CustomizeWorkerHandler加入操作和呼叫點
第5行當該Handler收到從Looper傳送過來的訊息,立即顯示ProcessingMessageDialog
第21行當該Handler處理完Message,關閉ProcessingMessageDialog

class CustomizeWorkerHandler extends Handler
{
    public void handleMessage(Message message)
    {
        showProcessingDialog();
        switch (message.what) {
            case 2:
                String result = HttpPageData.getPageData("https://www.google.com.tw/");
                setResultToMainActivity(result);
                break;
            case 3:
                Log.d(TAG, "message.arg1:" + message.arg1);
                Log.d(TAG, "message.arg2:" + message.arg2);
                break;
            case 4:
                CustomizeMessageObject messageObject = (CustomizeMessageObject) message.obj;
                Log.d(TAG, "MessageObject.name:" + messageObject.getName());
                Log.d(TAG, "MessageObject.type:" + messageObject.getType());
                break;
        }
        closeProcessingDialog();
    }
    private void showProcessingDialog()
    {
        mMainActivity.runOnUiThread(new Runnable() {
            @Override
            public void run()
            {
                mMainActivity.getProcessingMessageDialog().show();
            }
        });
    }
    private void closeProcessingDialog()
    {
        mMainActivity.runOnUiThread(new Runnable() {
            @Override
            public void run()
            {
                mMainActivity.getProcessingMessageDialog().dismiss();
            }
        });
    }
}

 

3.雙重Handler溝通

CustomizeWorkerHandler 的 showProcessingDialog 和 closeProcessingDialog方法其實是為了調用UI Thread的UI元件。
如果把處理UI Thread的內容集中寫在CustomizeWorkerHandler,會調用多次的 mMainActivity.runOnUiThread方法,
導致CustomizeWorkerHandler類別的冗長,其實可以在UI Thread中建立另一個UI Handler,當CustomizeWorkerHandler有結果產生時
藉由UI Handler發送訊息到UI Thread,訊息包含處理完的結果,把結果的處理轉回到 UI Thread中。
先新增UIHandler類別並建立實體,提供存取方法,把結果的處理放在handleMessage方法中,順便修正Magic Number

public class MainActivity extends ActionBarActivity implements OnClickListener
{
    ...
    public static final int GET_PAGE_DATA = 0;
    public static final int SEND_MESSAGE_ARGS = 1;
    public static final int SEND_MESSAGE_OBJECT = 2;
    private UIHandler mHandler;
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        ...
        initUIHandler();
        ...
    }
    private void initUIHandler()
    {
        mHandler = new UIHandler();
    }
    public UIHandler getHandler()
    {
        return mHandler;
    }
    class UIHandler extends Handler
    {
        @Override
        public void handleMessage(Message msg)
        {
            switch (msg.what) {
                case GET_PAGE_DATA:
                    String result = (String) msg.obj;
                    mResultTextView.setText(result);
                    break;
                case SEND_MESSAGE_ARGS:
                    mResultTextView.setText(""+msg.arg1+msg.arg2);
                    break;
                case SEND_MESSAGE_OBJECT:
                    CustomizeMessageObject messageObject = (CustomizeMessageObject) msg.obj;
                    mResultTextView.setText(""+messageObject.getName()+messageObject.getType());
                    break;
            }
            mProcessingMessageDialog.dismiss();
        }
    }

修改CustomizeWorkerHandler在handlMessage方法取得UI Handler,並使用UI Handler發送包含結果的訊息,

class CustomizeWorkerHandler extends Handler
{
    ...
    @Override
    public void handleMessage(Message message)
    {
        Message messageToUIHandler = mMainActivity.getHandler().obtainMessage(message.what);
        switch (message.what) {
            case MainActivity.GET_PAGE_DATA:
                messageToUIHandler.obj = HttpPageData.getPageData("https://www.google.com.tw/");
                break;
            case MainActivity.SEND_MESSAGE_ARGS:
                messageToUIHandler.arg1 = message.arg1;
                messageToUIHandler.arg2 = message.arg2;
                break;
            case MainActivity.SEND_MESSAGE_OBJECT:
                CustomizeMessageObject messageObject = (CustomizeMessageObject) message.obj;
                messageToUIHandler.obj = messageObject;
                break;
        }
        mMainActivity.getHandler().sendMessage(messageToUIHandler);
    }
}

如此就能在UI Thread中處理結果。