Android學習筆記11-消息處理機制和AsyncTask

Android學習筆記11-Service後臺服務(二)-異步消息處理機制和AsyncTask

一,消息機制的簡介

在Android中使用消息機制,首先想到的是Handler,Handler是Android消息機制的上層接口,Handler的使用方法很簡單,通過它可以把一個任務切換到Handler所在的線程中去執行,通常,Handler的使用場景就是更新UI。

如在上一章中所用的例子

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private TextView text;
    public static final int UPDATE_TEXT = 1;
    private Handler handler = new Handler() {
		@Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case UPDATE_TEXT:
                    //在這裏可以進行UI操作
                    text.setText("Nice to meet you");
                    break;
                default:break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //聲明控件
        Button changeText = (Button) findViewById(R.id.change_text);
        text = (TextView) findViewById(R.id.text);
        //設置點擊監聽器
        changeText.setOnClickListener(this);
    }

    /**
     * 設置點擊事件
     */
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.change_text:
                Message message = new Message();
                message.what = UPDATE_TEXT;
                handler.sendMessage(message);
                break;
            default:break;
        }
    }
}

在子線程中,進行耗時操作,執行完操作後,發送消息,通知主線程更新UI,這便是Android消息機制的典型應用場景.

二,消息機制的模型

消息機制主要包含

  • Message:需要傳遞的消息,可以傳遞數據(what,arg1,agr2,obj)

  • MessageQueue: 消息隊列。但是它的內部實現結構採用的的單鏈表的形式,因爲單鏈表在插入和刪除的操作上比較有優勢。它的主要功能是通過MessageQueue.enqueueMessage 方法向消息池中投遞消息,和用 MessageQueue.next 方法取走消息池中的消息。每個線程只有一個MessageQueue對象

  • Handler:消息輔助類,主要功能是向消息池 發送消息事件(Handler.sendMessage)和處理相應的消息事件(Handler.handleMessage)

  • Looper:它是每個線程中的MessageQueue的管家,調用Looper.loop()方法,就會進入無限循環中,每當發現MessageQueue中存在消息,就會將它取出,傳遞到Hanler.handleMessage() 方法中,每個線程只有一個Looper對象.

    異步消息處理的流程:
    首先我們在主線程中創建一個Handler對象,並重寫它的HandlerMessage方法.當我們需要更改UI時,我們創建一個Message對象,並通過Handler的sendMessage(message) 方法將消息發送出去,之後這條消息會被添加到MessageQueue的隊列中等待被處理,這時Looper會一直嘗試從MessageQueue中取出消息,最後分發回給handleMessage方法中,由於Handler對象是在主線程中創建的,所以handleMessage也會運行在主線程中。我們就可以安心的進行UI操作了。

三,使用AsyncTask

AsyncTask是一個抽象類,他是由Android封裝的一個輕量級異步類(輕量體現在使用方便,代碼簡潔),它可以在線程池中執行後臺任務,然後把執行的進度和最終的結果傳遞給主線程中並在主線程中更新UI。

AsyncTask的內部封裝了兩個線程池:

  • SerialExecutor :用於任務的排隊,讓多個需要執行的耗時任務,按順序排列。
  • THREAD_POOL_EXECUTOR :執行任務
    還封裝了一個Handler(InternalHandler) 用於從工作線程切換到主線程。

1,AsyncTask的泛型參數

AsyncTask的類聲明如下

public abstract class AsyncTask<Params, Progress, Result>

AsyncTask是一個泛型類,其中,三個泛型類型的參數的含義如下:

  • Params: 開始異步任務執行時傳入的參數類型。
  • Progress: 後臺任務執行時,如果需要在界面上顯示當前的進度,則使用這裏指定的泛型作爲單位。
  • Result: 當任務執行完畢後,如果需要對結果進行返回,則使用這裏指定的泛型作爲返回值類型。

比如自定義一個AsyncTask就可以寫成如下形式:

class DownloadTask extends AsyncTask<Void,Integer,Boolean>

如果AsyncTask確定不需要傳遞具體參數,那麼這三個泛型參數可以用Void代替。
有了這三個參數類型之後,也就控制了這個AsyncTask子類各個階段返回的類型,如果有不同的業務,我們就需要再另寫一個AsyncTask的子類進行處理。

2,AsyncTask的核心方法

  • onPreExecute()
    這個方法會在後臺任務開始執行之間調用,在主線程中調用,用於界面上一些初始化的操作,比如要顯示一個進度條對話框.

  • doInBackground(Params…)
    這個方法的所有代碼都會在子線程中運行,我們應該在這個方法中去處理耗時任務。
    任務一旦完成就可以通過return語句來將任務的執行結果進行返回,如果AsyncTask的第三個泛型參數指定的是Void,就可以不返回任務執行結果
    注意**在這個方法中不可以對UI進行操作,如果需要更新UI元素,比如說反饋當前任務的執行進度,可以調用publishProgress(Progress…)**方法。

  • onProgrssUpdate(Progress…)
    當在後臺調用了 publishProgress(Progress) 方法後,這個方法很快會被調用,方法中攜帶的參數是後臺任務中傳遞過來的 在這個方法中可以對UI進行操作,在主線程中進行 ,利用參數中的數值就可以對界面元素進行相應的更新。

  • onPostExecute(Result)
    doInBackground(Params…) 執行完畢並通過return語句進行返回時,這個方法就會很快被調用,參數中的Result類型與doInBackground方法的返回值有關。 可以利用這個返回的數據進行一些UI操作。 在主線程中運行,比如提醒任務的執行結果,以及關閉掉進度條的對話框。

上面幾個方法的調用順序依次是:
onPreExecute() -> doInBackground(Params…) -> publishProgress(Progress…) -> onProgrssUpdate(Progress…) -> onPostExecute(Result)

如果不需要執行更新進度則爲 onPreExecute() -> doInBackground(Params…) -> onPostExecute(Result)

除了上面的方法,AsyncTask還提供了onCancelled() 方法,它同樣在主線程中執行,當異步任務取消時,onCancelled() 會被調用,這個時候onPostExecute() 則不會被調用,但是要注意的是,AsyncTask中的cancel()方法並不是真正去取消任務,只是設置這個任務爲取消狀態 ,我們需要在doInBackground() 判斷終止任務。就好比想要終止一個線程,調用interrupt()方法,只是進行標記爲中斷,需要在線程內部進行標記判斷然後中斷線程。


package com.example.chen.androidthread;

import android.os.AsyncTask;
import android.widget.Toast;

public class DownloadTask extends AsyncTask <Void,Integer,Boolean>{

    @Override
    protected void onPreExecute() {
        progressDialog.show();
    }
    @Override
    protected Boolean doInBackground(Void... params) {
        try {
            while (true) {
            	//這個download()函數時虛構的一個返回下載進度的函數。
                int downloadPercent = doDownload();
                publishProgress(downloadPercent);
                if (downloadPercent >= 100) {
                    break;
                }
            }
        } catch (Exception e) {
            return false;
        }
        return true;
    }
    @Override
    protected void onProgressUpdate(Integer... values) {
        progressDialog.setMessage("當前下載進度:" + values[0] + " %");
                
    }
    @Override
    protected void onPostExecute(Boolean result) {
        progressDialog.dismiss();
        if (result) {
            Toast.makeText(context, "下載成功", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(context, "下載失敗", Toast.LENGTH_SHORT).show();
        }
    }
}

如果想要啓動這個任務,只需要簡單的調用
new DownloadTask().execute()方法。

在AsyncTask中,我們不需要Handler來發送和接收消息,只需要調用一個publishProgress()方法,就能輕鬆的從子線程切換到UI線程(主線程)了.

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