Android學習之異步操作處理機制

通過這篇文章你能學習到什麼?

  1. 通過子線程更新UI組件:handler
  2. AsyncTask

首先我們介紹handler。

一、異步消息處理的主要組成部分

  1. Messger

    在線程之間傳遞消息,而且其內部攜帶少量信息。用於在不同的線程之間交換數據。

  2. Handler

    用於發送和處理信息。一般發送信息使用其handler.sendmessger()方法,經過一系列的處理以後,最後會發送到handler的handleMessger()方法中。

  3. MessgerQueue

    用於存放所有handler發送的信息。這些信息會一直存放在隊列中,等待被處理。

  4. Looper

    looper相當於messgerQueue的管家。每當messgerQueue中存在一條信息時,就會調用looper中的loop()方法,將信息取出。並傳遞到handle的handleMessger()方法中。

二、代碼演示

我們主要想實現通過點擊按鈕,改變TextView中的顯示內容。
首先我們在xml文件中創建一個button以及一個textview。(代碼較爲簡單,就不展示了)
然後修改MainActiviy:

public class MainActivity extends AppCompatActivity {

    public static final int UPDATE_text = 1; //設置一個標識符

    private TextView textView;

    @SuppressLint("HandlerLeak")
    Handler handler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what){
                case UPDATE_text:
                    textView.setText("nihaoshijie"); //改變Ui組件內容
                    break;
            }
        }
    };

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

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { //設置一個點擊事件
            @Override
            public void onClick(View v) {
                new Thread(){  //設置一個子線程
                    @Override
                    public void run() {
                        Message message = new Message(); //設置一個messger對象
                        message.what = UPDATE_text;
                        handler.sendMessage(message);  //發送數據
                    }
                }.start();
            }
        });
    }
}

具體的代碼如上圖圖所示,下面我們來根據代碼分析下其異步處理的主要思路。

三、思路分析

首先我們需要明白一個點:

Android不推薦在子線程中直接更新UI組件,可能會出現不可知的錯誤。

而我們用handler異步處理機制。先在主線程中創建一個handler對象,並重寫其handleMessger方法。然後當子線程中需要對UI進行處理的時候,就會創建一個messger對象,並通過handler將其發送出去。之後這條信息就會被添加到MessgerQueue的隊列中等待被處理。而Looper會一直不斷的嘗試從MessgerQueue取出待處理信息,最後分發到Handler的handleMessger()方法中。由於handler是在主線程中創建的,所以handleMessger方法也在主線程中運行。這樣就能安心的對ui進行操作啦。

下面我們來介紹AsyncTask

一、基本使用

首先我們需要爲AsyncTask類指定三個泛型參數

  1. Params

    在執行AsyncTask需要傳遞的參數,可用於在後臺任務使用。

  2. Progress

    後臺任務執行時,如果需要在界面上顯示當前的進度,則使用這裏指定的泛型作爲進度單位。

  3. Result

    當任務執行完成以後,如果需要對結果進行返回,則使用這裏指定的泛型作爲返回值類型。

重寫幾個方法

  1. onPreExecute()

    這個方法在後臺任務開始執行之前調用,用於進行一些界面上的初始化操作。

  2. doInBackground()

    這個方法中的所有代碼都會在子線程中運行,我們應該在這個去處理所有的耗時任務。

  3. onProgressUpdate()

    這個方法可以對UI進行操作,利用參數中的數值就可以對界面元素進行相應的更新。

  4. onPostExecute()

    當後臺任務執行完畢並且用過return語句進行返回的時候,這個方法就會被調用。返回的數據會作爲參數傳遞到此方法中,可以利用一些返回的數據進行一些UI操作。

二、具體代碼演示

我們來製作一個倒計時的程序:

首先xml文件代碼:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <EditText
        android:id="@+id/et"
        android:layout_width="match_parent"
        android:layout_height="50dp" />
    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="倒計時"
        android:textSize="30sp"
        android:layout_gravity="center"
        />
    <Button
        android:id="@+id/bt"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:text="開始倒計時"
        android:textSize="20sp"/>

</LinearLayout>

然後是MainActivity中的代碼:

public class MainActivity extends AppCompatActivity {

    private EditText et;
    private TextView tv;
    private Button bt;

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

        bindID();

        bt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int time = Integer.parseInt(et.getText().toString());
                new MyTask().execute(time);
            }
        });

    }

    private void bindID() {

        et = findViewById(R.id.et);
        tv = findViewById(R.id.tv);
        bt = findViewById(R.id.bt);
    }

    @SuppressLint("StaticFieldLeak")
    class MyTask extends AsyncTask<Integer,Integer,String> {
        @Override
        protected String doInBackground(Integer... integers) {
            for (int i=integers[0];i>0;i--){
                try {
                    Thread.sleep(1000);
                    publishProgress(i);//調用onProgressUpdate方法
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return "計時結束";
        }

        @SuppressLint("SetTextI18n")
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            tv.setText(values[0]+"");
        }

        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
            tv.setText(s);
        }
    }
}

整體的代碼邏輯還是比較簡單的,主要是使用一下AsyncTask這個方法。

那麼對於Android的異步消息處理機制就說到這裏了。本來想加上一個RxJava的開源框架,但是篇幅有限。下次有機會再說~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章