Android 異步消息處理機制解析


Android 中的異步消息處理主要由四個部分組成,Message、Handler、MessageQueue、Looper。下面將會對這四個部分進行一下簡要的介紹。

1. Message:

  Message 是在線程之間傳遞的消息,它可以在內部攜帶少量的信息,用於在不同線程之間交換數據。通常使用 Message 的 what 字段攜帶命令,除此之外還可以使用 arg1 和

arg2 字段來攜帶一些整形數據,使用 obj 字段攜帶一個 Object 對象。

2. Handler:

  Handler 顧名思義也就是處理者的意思,它主要是用於發送和處理消息的。發送消息一般是使用 Handler 的 sendMessage()方法,而發出的消息經過一系列地輾轉處理后,最終會傳遞到 Handler 的 handlerMessage()方法中。

3. MessageQueue:

  MessageQueue 是消息隊列的意思,它主要用於存放所有通過 Handler 發送的消息。這部分消息會一直存在於消息隊列中,等待被處理。每個線程中只會有一個 MessageQueue 對象。

4. Looper:

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

------------------------------異步消息處理流程--------------------------------------

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    public static final int UPDATE_TEXT = 1;

    private TextView text;
    private Button changeText;

    private Handler handler = new Handler() {


        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);
        text = (TextView) findViewById(R.id.text);
        changeText = (Button) findViewById(R.id.change_text);
        changeText.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.change_text:
                new Thread(new Runnable() {

                    @Override
                    public void run() {
                        //子線程中改變UI導致程序崩潰
                        //text.setText("Nice to meet you");

                        Message message = new Message();
                        message.what = UPDATE_TEXT;
                        //將Message對象發送出去
                        handler.sendMessage(message);
                    }
                }).start();
        }
    }
}
Message使用示例

  首先需要在主線程當中創建一個 Handler 對象,並重寫 handleMessage() 方法。

  然后當子線程中需要進行UI操作時,就創建一個 Message 對象,並通過 Handler 將這條消息發送出去。

  之后這條消息會被添加到 MessageQueue 的隊列中等待被處理,而 Looper 則會一直嘗試從 MessageQueue 中取出待處理消息

  最后分發回 Handler 的 handleMessage() 方法中。

  由於 Handler 是在主線程中創建的,所以此時 handleMessage() 方法中的代碼也會在主線程中運行,於是就可以安心地進行UI操作了。

一條 Message 經過這樣一個流程的輾轉調用后,也就從子線程進入到了主線程,從不能更新 UI 變成了可更新 UI,整個異步消息處理的核心思想也就如此。

 

----------------------使用AsyncTask----------------------------

為了更加方便我們在子線程中對 UI 進行操作,Android 還提供了另外一些好用的工具,AsyncTask 就是其中之一。借助 AsyncTask,即使你對異步消息處理機制完全不了解,也可以十分簡單地從子線程切換到主線程。當然,AsyncTask 背后的實現原理也是基於異步消息處理機制的,只是 Android 做了很好的封裝而已。

AsyncTask 的基本用法:

  首先來看一下 AsyncTask 的基本用法,由於 AsyncTask 是一個抽象類,所以如果我們想使用它,就必須創建一個子類去繼承它。

  在繼承時我們可以為 AsyncTask 類指定三個泛型參數,這三個參數的用途如下:

    Params:在執行 AsyncTask 時需要傳入的參數,可用於在后台任務中使用。

    Progress:后台任務執行時,如果需要在界面上顯示當前的進度,則使用這里指定的泛型作為進度單位。

    Result:當任務執行完畢后,如果需要對結果進行返回,則使用這里指定的泛型作為返回值類型。

/**
 * AsyncTask泛型參數解釋
 * Void 表示在執行AsyncTask的時候不需要傳入參數給后台任務
 * Integer 表示使用整形數據來作為進度顯示單位
 * Boolean 表示使用布爾類型數據來反饋執行結果
 */
class DownloadTask extends AsyncTask<Void, Integer, Boolean> {

    @Override
    protected Boolean doInBackground(Void... params) {
        return null;
    }
}
一個簡單的自定義AsyncTask

  接着我們還需要重寫 AsyncTask 中的幾個方法才能完成對任務的定制。

    onPreExecute():

      這個方法會在后台任務開始執行之前調用,用於進行一些界面上的初始化操作,比如顯示一個進度條對話框等。

    doInBackground(Params...):

      這個方法中的所有代碼都會在子線程中運行,我們應該在這里去處理所有的耗時任務。注意,在這個方法中是不可以進行 UI 操作的。

    onProgressUpdate(Progress...):

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

    onPostExecute(Result):

      當后台任務執行完畢並通過 return 語句進行返回時,這個方法就很快會被調用。返回的數據會作為參數傳遞到此方法中,可以利用返回的數據來進行一些 UI 操作,比如提醒任務執行的結果,以及關閉掉進度條對話框等。

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

    @Override
    protected void onPreExecute() {
        //顯示進度對話框
//        ProgressDialog.show();
    }

    @Override
    protected Boolean doInBackground(Void... params) {
        try {
            while (true) {
                int downloadPercent = 0;
                //這是一個虛構的方法
//                downloadPercent = doDownload;
                publishProgress(downloadPercent);
                if (downloadPercent >= 100) {
                    break;
                }
            }
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        //在這里更新下載進度
//        progressDialog.setMessage("Downloaded " + values[0] + "%");
    }

    @Override
    protected void onPostExecute(Boolean result) {
        //關閉進度對話框
//        progressDialog.dismiss();
        //在這里提示下載結果
        if (result) {
//             Toast.makeText(context,"Download succeeded",Toast.LENGTH_SHORT).show();
        } else {
//            Toast.makeText(context,"Download failed",Toast.LENGTH_SHORT).show();
        }
    }
}
完整AsyncTask示例

簡單來說,使用 AsyncTask 的訣竅就是,在 doInBackground() 方法中去執行具體的耗時任務,在 onProgressUpdate() 方法中進行 UI 操作,在 onPostExecute()方法中執行一些任務的收尾工作。

  如果想要啟動這個任務,只需編寫以下代碼即可:

    new DownloadTask.execute();

以上就是 AsyncTask 的基本用法。我們並不需要去考慮異步消息處理機制,也不需要專門使用一個 Handler 來發送和接收消息,只需要調用一下 publishProgress()方法就可以輕松地從子線程切換到 UI 線程了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM