2.3 Thread Communication -- Android Message Passing

Android Message Passing

1. Message Passing機制介紹

之前介紹的線程間通信方式 - Pipe,Shared Memory,Blocking Queue,都會導致線程阻塞,因此影響Android UI線程的刷新,Android通過Message Passing的機制實現了一種無阻塞的生產者-消費者模式。
相關的類:
這裏寫圖片描述

Message被生產者線程插入到MessageQueue中,消費者線程從MessageQueue中取出Message並處理。流程:
這裏寫圖片描述
1. Insert:生產者線程通過使用連接消費者的Handler把message插入到隊列中。
2. Retrieve(檢索/取回/恢復):消費者線程中的Looper運行並從順序的隊形中取出message。
3. Dispatch(派遣/分派):那些Handler負責處理消費者線程中的message消息。一個線程中可以有多個Handler實例來處理message消息;這個Looper確保了message消息被分發到正確的Handler中。


這裏寫圖片描述


Demo

/**
 * 本例使用Android的Message機制實現生產者-消費者問題。
 * 本例中UI Thread爲生產者,它通過子線程中的Handler向子線程的Message Queue中發送消息;
 * 子線程爲消費者,它通過Looper取回並分發到子線程中的Handler中處理message。
 */
public class LooperActivity extends Activity {

    private static final String TAG = LooperActivity.class.getSimpleName();

    private LooperThread mLooperThread = null;
    private Button mMessagePassingBtn = null;

    private static class LooperThread extends Thread {

        public Handler mHandler = null;

        @Override
        public void run() {
            // 1. 初始化子線程中的Looper。注:Android的UI Thread中默認有Looper,因此不需要顯式的初始化。
            Looper.prepare();

            // 2. 實例化Handler,用來處理髮送過來的message
            mHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    if (msg.what == 0) {
                        // TODO 在子線程中做耗時的操作
                        Log.i(TAG, "LooperThread # handleMessage # msg.what is 0");
                    }
                }
            };

            // 3. 把Message Queue中的message分發給消費者線程,即本例中的子線程
            Looper.loop();
        }
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_message);

        mMessagePassingBtn = (Button) findViewById(R.id.message_passing_btn);

        mLooperThread = new LooperThread();
        mLooperThread.start();

        mMessagePassingBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mLooperThread.mHandler != null) {
                    // UI Thread調用子線程中的Handler把message發送的MessageQueue中
                    Message msg = mLooperThread.mHandler.obtainMessage(0);
                    mLooperThread.mHandler.sendMessage(msg);
                }
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 關閉子線程的Looper,停止分發MessageQueue中的Message,因此使得Thread的run()方法結束,子線程停止
        mLooperThread.mHandler.getLooper().quit();
    }
}

2. Message Passing使用到的類

MessageQueue

這個message queue對應的類是android.os.MessageQueue,它是一個沒有約束的,單方向的鏈接List。message queue中的message是根據時間戳來排序的,時間越小越靠前,當一個message的時間戳比當前時間小的時候纔會被分發出去。
這裏寫圖片描述
生產者線程可以隨時插入一個新的message到message queue中,而新插入的message位置由message的時間戳確定的。
當message queue中的message爲空了,消費者線程中的Looper無法取出message時,那麼消費者線程就會一直阻塞,當又有新的message時,消費者線程就會再次執行。

MessageQueue.IdelHandler

如果沒有message處理的話,消費者線程會有一些idle time(空閒時間)。默認情況下消費者線程在idle time期間會等待新的message,但是我們可以利用這段時間去執行其他的任務。
這裏寫圖片描述
使用Idle Time
我們事先android.os.MessageQueue.IdleHandler接口,在queueIdle()方法中處理其他任務。
這裏寫圖片描述

queueIdle()方法返回的是boolean類型,
true:保留這個idle handler,即不會移除這個IdleHandler。當在有Idle Time時還會繼續調用這個IdleHandler。
false:移除這個idle handler,即只需要執行一遍,和MessageQueue.removedIdleHandler()方法的效果一樣。

IdleHandler其他介紹的文章:http://bbs.51cto.com/thread-1094228-1.html

3. Message

Message分爲兩種:Data Message 和 Task Message。

Data Message

Data message 可以設置多個參數,然後發送到消費者線程中。
參數介紹:
這裏寫圖片描述

Task Message

此Task是一個java.lang.Runnable對象,在消費者線程中執行。Task message 只能包含這個Task。
介紹:
這裏寫圖片描述

處理Message

如果是Data message,消費者線程直接處理這個Data。
如果是Task message,會讓Runnable在消費者線程中執行,但是消費者線程不會接收要在Handler.handleMessage(Message)方法中處理的message,因爲這是Data message。

Message聲明週期

這裏寫圖片描述
這個傳輸的狀態部分被應用控制,部分被平臺控制。這些狀態不是區分顯著的。

Initialized

這裏寫圖片描述

Pending

message已經被生產者線程插入到message queue中,在等待被分發到消費者線程。

Dispatched

在這個狀態,Looper會把message從message queue中取出並分發到消費者線程中處理。
這個分發操作是由Looper控制的,不受application的影響。

Recycled

到這個狀態時,message state會被清除,然後message實例被放入message pool中。
當message在消費者線程中執行完畢了,Looper會處理message的回收操作。
Recycling of messages is handled by the runtime and should not be done explicitly by the application.

4. Looper

當Looper被添加到Thread上面時,message queue也被添加到這個Thread上面了。Looper管理着message queue和分發message到消費者線程。
UI Thread默認有Looper,而在應用中其他的子線程需要顯式的創建Looper。
一個線程只能有一個相關聯的Looper,一個Message Queue。多個生成者線程把message都發送到這個Message Queue中,然後消費者線程按順序的執行這些message。
需要耗時操作的message最好不應使用,因爲它會導致message queue中其他重要的任務延遲執行。

Looper termination

終止Looper的相關方法:quit()和quitSafely()。
quit():終止Looper中所有的message,包括message queue中的pending message和已經passed the dispatch barrier(界線/屏障)。
quitSafely():只是終止那些沒有passed the dispatch barrier的message。Pending message不受影響,可以正常分發並處理。
Looper和Thread的關係
終止Looper不會終止Thread,它僅僅是終止了Looper.loop()操作。但是Thread不可以再開啓舊的Looper,也不可以再創建一個新的Looper,因此這個Thread不能再enqueue(入隊)或者處理message了。如果你調用了Looper.prepare(),那麼它將會拋出RuntimeException,這是因爲這個Thread已經有了一個相關聯的Looper了。如果你調用Looper.loop(),它將會阻塞,但是沒有消息從這個message queue被分發出去。
UI Thread Looper
UI Thread 默認已經關聯上一個Looper。
UI Thread Looper和其他Thread Looper的不同點(UI Thread Looper的特點):
這裏寫圖片描述

5. Handler

如果沒有Looper的話,Handler就不能正常工作,Handler不能和message queue結合插入message,也不能接收到message然後處理。Handler實例在創建的時候已經和Looper綁定了。
* Handler構造方法中沒有綁定當前Thread中的Looper(可以調用Looper.prepare()方法綁定):

new Handler();
new Handler(Handler.Callback);
  • Handler構造方法中顯式的綁定Looper:
new Handler(Looper);
new Handler(Looper, Handler.Callback);

一個Thread中可以有多個Handler。多個Handler中不能並行執行,message一直在同一個隊列中並且是順序處理的。
這裏寫圖片描述

Message creation

這裏寫圖片描述

Message insertion

這裏寫圖片描述
Message插入到Message Queue時可能會出的錯誤:
這裏寫圖片描述
Demo:使用兩種Message(Task Message和Data Message)

public class HandlerActivity extends Activity {

    private final static int HIDE_PROGRESS_BAR = 0;
    private final static int SHOW_PROGRESS_BAR = 1;
    private static UIHandler uiHandler;

    private Button handlerTestBtn = null;
    private TextView showText = null;
    private ProgressBar progressBar = null;

    private BackgroundThread myBackgroundThread = null;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);

        // 實例化UI Thread Handler
        uiHandler = new UIHandler(HandlerActivity.this);

        // 實例化子線程並開啓
        myBackgroundThread = new BackgroundThread();
        myBackgroundThread.start();

        handlerTestBtn = (Button) findViewById(R.id.handler_test_btn);
        showText = (TextView) findViewById(R.id.show_text);
        progressBar = (ProgressBar) findViewById(R.id.progress_bar);

        handlerTestBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                myBackgroundThread.doWork();
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        myBackgroundThread.exit();
    }

    // 使用Activity的弱引用,防止Handler不釋放Activity中的Component,導致內存泄漏
    private static class UIHandler extends Handler {
        private final WeakReference<HandlerActivity> activity;

        public UIHandler(HandlerActivity handlerActivity) {
            activity = new WeakReference<HandlerActivity>(handlerActivity);
        }

        @Override
        public void handleMessage(Message msg) {
            HandlerActivity myActivity = activity.get();
            if (myActivity != null) {
                switch (msg.what) {
                    case HIDE_PROGRESS_BAR:
                        myActivity.showText.setText("耗時(ms):" + msg.arg1);
                        myActivity.progressBar.setVisibility(View.GONE);
                        break;

                    case SHOW_PROGRESS_BAR:
                        myActivity.progressBar.setVisibility(View.VISIBLE);
                        break;
                }
            }
        }
    }

    // 子線程模擬處理耗時操作,使用子線程中的Handler.post()把Task Message發送到UI Thread中處理
    private static class BackgroundThread extends Thread {
        // 子線程的Handler
        private Handler backgroundHandler = null;

        @Override
        public void run() {
            Looper.prepare();
            backgroundHandler = new Handler();
            Looper.loop();
        }

        public void doWork() {
            // 子線程的Handler發送一個Task Message
            backgroundHandler.post(new Runnable() {
                @Override
                public void run() {
                    // 使用UIHandler把Data Message發送到UI Thread,然後顯示ProgressBar
                    Message uiMsg = uiHandler.obtainMessage(SHOW_PROGRESS_BAR, 0, 0, null);
                    uiHandler.sendMessage(uiMsg);

                    // 模擬耗時操作
                    Random random = new Random();
                    int randomInt = random.nextInt(3000);
                    SystemClock.sleep(randomInt);

                    // 使用UIHandler把Data Message發送到UI Thread,然後顯示耗時時間,隱藏ProgressBar
                    uiMsg = uiHandler.obtainMessage(HIDE_PROGRESS_BAR, randomInt, 0, null);
                    uiHandler.sendMessage(uiMsg);
                }
            });
        }

        public void exit() {
            backgroundHandler.getLooper().quit();
        }
    }
}

Message Processing(處理)

Looper分發的message會被消費者線程中的Handler處理。

兩種message(Task Message和Data Message)處理方式:

  • Task message
    Task message中只包含一個Runnable,而沒有數據。因此,需要處理執行的部分在Runnable的run()方法中,它會在消費者線程中自動執行,而不會調用Handler.handleMessage()方法。
  • Data message
    當message中帶有數據時,這個Handler會接收這個數據並負責處理。消費者線程通過重寫(override) Handler.handleMessage(Message msg)方法來處理數據。

兩種方式處理Data message數據

第一種

創建Handler對象時,重寫handleMessage()方法。
handleMessage()方法應該在message queue可訪問之後(在Looper.prepare()方法調用之後),並且在獲取message之前(在Looper.loop()方法調用之前)被定義。
Eg:

class ConsumerThread extends Thread {
    Handler mHandler = null;

    @Override
    public void run() {
        Looper.prepare();
        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // TODO 處理Data message
            }
        };
        Looper.loop();
    }
}

第二種

實現Handler.Callback接口,此接口中定義了一個返回boolean類型的handleMessage()方法。

public interface Callback {
    public boolean handleMessage(Message msg);
}

使用這種方式,當我們實例化Handler對象是,需要把Callback傳進Handler的構造方法中。
Eg:

public class HandlerCallbackActivity extends Activity implements Handler.Callback {
    private Handler uiHandler = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 在Handler構造方法中傳進Handler.Callback
        uiHandler = new Handler(this);
    }

    // 重寫Handler.Callback的方法
    @Override
    public boolean handleMessage(Message msg) {
        // TODO 處理Data message
        return true;
    }
}

Callback.handleMessage()方法返回boolean類型值的意思:
true:表示這個message被處理;
false:這個message被傳遞Handler.handleMessage()方法中處理。
注意:
Handler.Callback不會重寫Handler.handleMessage()方法。Handler.Callback的方法是先於Handler.handleMessage()方法被調用的。Handler.Callback可以在Handler.handleMessage()獲得message之前截獲到並且可以修改message。
Demo

public class HandleMessageActivity extends Activity implements Handler.Callback {

    private final String TAG = HandleMessageActivity.class.getSimpleName();
    private Button handleMessageBtn = null;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handlemessage);

        handleMessageBtn = (Button) findViewById(R.id.handle_message_test_btn);
        handleMessageBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                onHandlerCallback();
            }
        });
    }

    // 實現Handler.Callback中的方法
    @Override
    public boolean handleMessage(Message message) {
        switch (message.what) {
            case 1: // 截獲並修改message內容,返回true,因此在這個方法中message被處理了,傳不到Handler.handleMessage()方法
                message.what = 11;
                Log.i(TAG, "Callback截獲並修改message.what(1 -> 11),Callback -> handleMessage: " + message.what);
                return true;

            case 2: // 截獲並修改message內容,返回false,因此會傳到Handler.handleMessage()方法中處理這個被修改的message
                message.what = 22;
                Log.i(TAG, "Callback截獲並修改message.what(2 -> 22),Callback -> handleMessage: " + message.what);
                return false;

            default:
                return false;
        }
    }

    public void onHandlerCallback() {
        // 實例化Handler對象時傳進Callback,創建關聯
        Handler handler = new Handler(this) {
            @Override
            public void handleMessage(Message msg) {
                // Handler.Callback中的方法handleMessage()會在Handler.handleMessage之前截獲到message,並且可以修改這個message內容,
                // 當Handler.Callback中的方法handleMessage()返回false的話,會傳到這裏處理;
                // 當Handler.Callback中的方法handleMessage()返回true的話,那麼這個message會被處理,而傳不到這個方法。
                Log.i(TAG, "Handler -> handleMessage: " + msg.what);
            }
        };

        handler.sendEmptyMessage(1);
        handler.sendEmptyMessage(2);
    }
}

6. Removing Messages from the Queue

Message identifiers(標識符)

這裏寫圖片描述

從message queue中移除message的方法

這裏寫圖片描述
Object標識符可以被用到Data message和Task message,因此可以通過這個方式把message分到一個Tag,這樣能方便我們移除同一個Object Tag的message。
注意:一旦這個message已經被分發出去了,那麼把這個message放入隊列的生產者線程就不能停止這個message的處理,即已分發出去的message會被正常的處理掉

Object tag = new Object();

Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        // TODO 處理message
    }
};

// Tag爲Object標識符的Data message
Message msg = handler.obtainMessage(0, tag);
handler.sendMessage(msg);

// Tag爲Object標識符的Task message
handler.postAtTime(new Runnable() {
    @Override
    public void run() {

    }
}, tag, SystemClock.uptimeMillis());

// 移除同一個Object Tag的所有message
handler.removeCallbacksAndMessages(tag);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章