1. 程式人生 > >Handler,Looper,MessageQueue三劍客的合作方式

Handler,Looper,MessageQueue三劍客的合作方式

三劍客各自的絕招

Handler在訊息傳遞機制裡扮演著很重要的角色,它是用來發送訊息與處理訊息的。
MessageQueue則是用來儲存Message資訊的佇列,它採用的是先進先出的方式。
Looper則用來負責讀取MessageQueue中的Message訊息的,在讀到訊息後再將訊息改善給該訊息的Handler進行處理。

三劍客的關係及合作的方式

首先我們來看看Looper的構造器程式碼
private Looper
{
    mQueue = new MessageQueue();
    mRun = true;
    mThread = Thread.currentThread();
}
從上面我們可以看到當我們新建初始化一個Looper的時候,預設就會幫我們加上一個與之對應的MessageQueue,想想也是,如果沒有對應的訊息佇列,那麼Looper還怎麼讀取得到資訊呢?
再進一步,Handler是用來發送訊息的,那麼訊息一旦傳送出去,就像信封發出去的時候,你需要一個信箱吧,那麼android裡面的訊息傳遞機制裡面信箱的角色是由誰來扮演的呢?這個MessageQueue就當仁不讓地承擔下這個重任了。
既然Handler離不開MessageQueue,而MessageQueue又是由Looper生成,那麼可以說Handler也就跟Looper有著難捨難分的關係了。由此可以看出,它們三者合而生,分則完。只有三者三劍合璧,才能將訊息傳遞的任務給做好。
或者有人會問,那麼為什麼有時在主執行緒裡面看不到Looper與MessageQueue的身影呢,只要定義Handler也可以傳送訊息與處理訊息?其實這個是因為主執行緒裡面已經提前給我們定義了一個Looper物件,也就有了相應的MessageQueue。這樣,我們當然可以只定義Handler這一個了。但如果我們要想讓自己定義的執行緒也加入到整個訊息傳遞裡面來,毫無疑問,我們需要自己一個Looper物件,才能正常地傳送並處理訊息。

接下來就讓我們來看看一個自定義的執行緒的處理的資訊。

//定義一個自定義的需要改變UI介面的執行緒,
    class MyThread extends Thread{
        public Handler mHandler;
        public void run(){
            //呼叫looper的prepare()可以進行Looper的初始化
            Looper.prepare();
            mHandler = new Handler(){
                //定義處理訊息的方法
                @Override
                public
void handleMessage(Message msg){ if(msg.what == 0x123){ //在此處可以定義自己要執行的程式碼,一般為請求網路資料或者計算量大的計算 Message mainMessage = new Message(); //都可以將message的what賦值為0x123,並不會互相干擾,因為它們隸屬不同的messageQueue,因為它們由不同的handler傳送。handler傳送訊息到指定的message裡面。這裡是將引數發回主執行緒去修改UI介面
mainMessage.what = 0x123; Bundle mainBundle = new Bundle(); //可以在message裡面放許多的資料,比如ArrayList,借用bundle mainBundle.putIntegerArrayList("nums",(ArrayList)nums); mainMessage.setData(mainBundle); MainActivity.this.mainHandler.sendMessage(mainMessage); } } }; //Looper.loop()執行後,Looper開始不斷迴圈從MessageQueue讀取訊息。 Looper.loop(); } }
myThread = new MyThread();
        //啟動新執行緒,當執行緒啟動後,執行緒裡面的訊息迴圈也就開始執行,並時刻接受來自於myThread.handler傳送來的訊息。
        listPrimeThread.start();
Message msg = new Message();
        msg.what = 0x123;
        Bundle bundle = new Bundle();
        bundle.putInt(UPPER_NUM,
                Integer.parseInt(et.getText().toString()));
        msg.setData(bundle);
        //向自定義的執行緒中的Handler傳送訊息
        myThread.mHandler.sendMessage(msg);
我們需要定義一個Handler來接受來自自定義執行緒發來的修改UI介面的訊息。必須清楚的是主執行緒與自定義執行緒是不同的執行緒,它們各自擁有一套訊息傳遞機制。
Handler mainHandler = new Handler(){
        public void handleMessage(Message msg){
        //msg.what賦值為一樣的值不會有所幹擾。
            if(msg.what == 0x123){
                et = (EditText) findViewById(R.id.et);
                ArrayList<Integer>nums = msg.getData().getIntegerArrayList("nums");
                et.setText(nums.toString());
            }
        }
    };

順便提一提,如果我們直接在剛才自定義的myThread裡面通過下面語句修改UI介面,雖然編譯通過,但執行時會報一個錯誤。如圖所示,這意味著你在除主執行緒外修改了UI介面,這個是android裡面不允許的。所以有關UI介面的修改都要返回到主執行緒去修改。
EditText et = (EditText) MainActivity.this.findViewById(R.id.et);
et.setText(nums.toString());
這裡寫圖片描述