Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android studio for android learning (十九 ) 最新Handler消息傳遞機制全解

android studio for android learning (十九 ) 最新Handler消息傳遞機制全解

編輯:關於Android編程

1.Android制定了一條簡單的原則:只允許UI線程(亦即主線程)修改Activity中的UI組件。當一個程序第一次啟動時,Android會同時啟動一條主線程,主線程主要負責處理與UI相關的事件,如用戶的按鍵事件、用戶接觸屏幕的事件、屏幕繪圖事件,並把相關的事件分發到相應的組件進行處理,所以主線程通常又叫做UI線程。


Handler存在的意義就是一個消息機制, 可以在一個線程中創建並在另一個線程中觸發


2.Handler的作用:

(1)在一個線程中發送消息。 (2)在另一個線程中獲取、處理消息。

3.Handler類包含如下方法用於發送、處理消息(這裡只列出了常用的部分,還有更多可以自己去找):

? void handlerMessage(Message msg):處理消息的方法,該方法通常用於被重寫。 ? final boolean hasMessage(int what):檢查消息隊列中是否包含what屬性為指定值的消息。 ? sendEmptyMessage(int what):發送空消息 ? final boolean sendMessage(Message msg):立即發送消息,注意這塊返回值,如果message成功的被放到message queue裡面則返回true,反之,返回false;

4.在被調用線程中完成以下內容:

(1)調用 Looper的prepare()方法為當前線程創建Looper對象,創建Looper對象時,它的構造器會創建與之配套的MessageQueue。

(2)有了Looper之後,創建Handler子類的實例,重寫HandlerMessage()方法,該方法負責處理來自其它線程的消息。

(3)調用Looper的loop()方法啟動Looper。

注:若被調用線程是主線程類,由於系統自動為主線程創建了Looper的實例,因此第一、三步驟可省略,而只需要做第2步即可。

在調用線程中完成:

(1)創建message,並填充內容。

(2)使用被調用類創建的Handler實例,調用sendMessage(Message msg)方法。

5.下面這個例子是Handler在主線程中獲取,處理消息,在子線程中發送消息,這個例子handler寫在主線程中。

main.xml




    

main.java

package com.dragon.testfuction;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
import java.util.Timer;
import java.util.TimerTask;


public class Main extends AppCompatActivity  {
//    定義圖片顯示ID
    int[] imageIds = new int[]{
            R.drawable.one,
            R.drawable.two,
            R.drawable.three,
            R.drawable.four
    };
    int currentImageId = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        final ImageView show = (ImageView) findViewById(R.id.show);
        final Handler myHandler = new Handler(){
            @Override
            public void  handleMessage(Message msg){
//                檢查消息發送來源,如果是本程序發送的
                if(msg.what == 0x1233){
//                    動態修改圖片
                    show.setImageResource(imageIds[currentImageId++%imageIds.length]);
                }
            }
        };
//定義一個定時器,周期性的執行指定任務
        new Timer().schedule(new TimerTask(){
            @Override
            public void run(){
            //在子線程中拿到父線程中創建的handle對象,通過該對象來向父線程的消息隊列發送消息(通過這種方式,可以在其它子線程中與主線程通信來由UI線程更新界面)
                myHandler.sendEmptyMessage(0x1233);
            }
    },0,1200);
}
}

6。為避免ANR,應該在子線程中執行耗時較長的操作,而此操作完成後,有可能需要通知主線程修改UI。在子線程中執行耗時任務後,通知主線程修改UI組件的例子:使用新進程計算質數,並用Toast顯示這個例子是在主線程中發送消息,在子線程中獲取,處理消息(例子來自瘋狂java講義),這個例子handle寫在子線程中。

main.xml





    
    

main.java

package com.dragon.testfuction;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;


public class Main extends AppCompatActivity  {
    static final String UPPER_NUM = "upper";
    EditText etNum;
    CalThread calThread;
//    定義一個線程類
    class CalThread extends Thread {
            public Handler mHandler;
            public void run(){
//                創建looper對象,每一個線程使用Handle都要有一個looper對象
                Looper.prepare();
//                子線程中定義handler獲取處理消息
                mHandler = new Handler(){
//                    定義處理信息的方法
                    @Override
                    public void handleMessage(Message msg){
                            if(msg.what == 0x123){
                                int upper = msg.getData().getInt(UPPER_NUM);
                                List nums = new ArrayList();
                                outer:
//                                質數也是素數,除了1和它本身外,不能被其它整除
                                for(int i =2;i <=upper;i++){
                                    for(int j=2; j<= Math.sqrt(i);j++){
//                                        如果可以整除,說明不是質數
                                        if(i!=2 && i%j==0){
                                            continue outer;
                                        }
                                    }
                                    nums.add(i);
                                }
//                                用Toast顯示所有統計出來的質數
                                Toast.makeText(Main.this,nums.toString(),Toast.LENGTH_LONG).show();
                            }
                    }
                };
                Looper.loop();//啟動looper
            }
    }
    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        etNum = (EditText) findViewById(R.id.etNum);
        calThread = new CalThread();
        calThread.start();//啟動新線程
    }
//按鈕事件點擊處理函數
    public void cal(View source){
//        創建消息
        Message msg = new Message();
        msg.what = 0x123;
        Bundle bundle = new Bundle();
        bundle.putInt(UPPER_NUM,Integer.parseInt(etNum.getText().toString()));
        msg.setData(bundle);
//        在主線程中想新線程中的Handler發送消息
        calThread.mHandler.sendMessage(msg);//在主線程中發送消息
    }
}

7.總結:

這裡寫圖片描述

這裡寫圖片描述

這裡寫圖片描述

8.注意

UI線程:就是我們的主線程,系統在創建UI線程的時候會初始化一個Looper對象,同時也會創建一個與其關聯的MessageQueue; Handler:作用就是發送與處理信息,如果希望Handler正常工作,在當前線程中要有一個Looper對象 Message:Handler接收與處理的消息對象 MessageQueue:消息隊列,先進先出管理Message,在初始化Looper對象時會創建一個與之關聯的MessageQueue; Looper:每個線程只能夠有一個Looper,管理MessageQueue,不斷地從中取出Message分發給對應的Handler處理!

looper.prepare提供源碼如下:

  /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

9.根據上面兩個例子,大家注意區分寫在主線程中和子線程中的區別

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved