亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

全部開發者教程

Android 入門教程

菜單類控件
菜單:Menu
并發編程
多線程

AsyncTask:異步任務

在前面的章節有提到過,Android 系統默認會在主線程(UI 線程)執行任務,但是如果有耗時程序就會阻塞 UI 線程,導致頁面卡頓。這時候我們通常會將耗時任務放在獨立的線程,然后通過 Handler 等線程間通信機制完成 UI 的刷新。很多時候我們也許只是想執行一個簡單的任務,為此寫一套 Handler 線程通信就會顯得比較復雜,不用擔心,Android 系統為我們提供了一個專門用于執行異步任務的工具——Async Task,它可以讓我們執行后臺任務并輕松的與 UI 線程進行狀態同步,今天就一起來學習一下 AyncTask 的用法。

1. AsyncTask 簡介

AsyncTask 類通常用來在執行后臺任務的同時刷新 UI,通過調用execute()方法觸發后臺任務的執行,首先會回調 AsyncTask 的onPreExecute(),接著回調doInBackground()來執行自定義的后臺任務,最后回調onPostExecute()方法用來刷新 UI,整體流程示意圖如下:

圖片描述

2. AsyncTask 的基本用法

2.1 聲明 AsyncTask

我們不能直接創建 AsyncTask,正確的方法是繼承自 AsyncTask 實現一個它的子類,如下:

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
        protected Long doInBackground(URL... urls) {
             // 執行后臺耗時任務
            return;
        }

        protected void onProgressUpdate(Integer... progress) {
            // 任務執行進度更新
        }

        protected void onPostExecute(Long result) {
            // 執行完畢,更新UI
        }
    }

以上是 AsyncTask 的核心回調方法,每個方法的含義會在后面具體講到。

2.2 指定參數

AsyncTask 可以幫助你在子線程和主線程之間同步參數,根據不同的業務場景,參數類型和個數也會不一樣。剛剛在 2.1 小節聲明 AyncTask 子類的時候,需要傳入 3 個泛型參數:

  • TypeOfVarArgParams:
    在任務啟動之后,傳入給后臺任務的參數類型

  • ProgressValue:
    啟動之后到任務結束之間,系統會不斷回調此方法,用來更新任務的進度

  • ** ResultValue:**
    后臺任務的執行結果

2.3 啟動后臺任務

在聲明完后臺任務之后,就可以直接啟動了。啟動方式比較簡單,直接通過調用execute()方法啟動后臺任務:

new DownloadFilesTask().execute(url1, url2, url3);

3 AsyncTask 關鍵回調方法

AsyncTask 是由 4 個回調方法配合組成,這 4 個回調方法按照一定的順序依次被調用,所以我們需要 focus 的是在正確的回調方法中實現我們想要的邏輯,下面詳細看看如何使用。

  • onPreExecute():
    在執行execute()方法之后該方法立即被調用,標志著 AyncTask 正式開啟。通常用來做一些需要在后臺任務開啟之前完成的初始化工作,比如展示一個進度條、或者彈出一個對話框等等。該方法在 UI 線程執行。
  • doInBackground(Params):
    在執行完onPreExecute()方法之后立即被調用,用來執行需要放在后臺執行的耗時任務。在創建 AsyncTask 的時候傳入的參數就是提供給doInBackground使用的。在后臺任務執行完畢后,還需要將執行結果返回到onPostExecutes ()中,同時我們也可以通過publishProgress(Progress…)方法來手動發布任務進度,進度將從子線程發送到 UI 線程。毋庸置疑,該方法在子線程中執行。
  • onProgressUpdate(Progress…):
    當我們通過publishProgress(Params)發布進度之后,系統會回調該方法,用來獲取任務執行進度并更新 UI。這一步就完成了子線程到主線程的通信,該方法在 UI 線程執行
  • onPostExecute(Result):
    當后臺任務執行完畢,該方法被回調,同時標志著整個 AyncTask 結束。與onPreExecute相反,通常會在onPostExecute中做一些回收工作,比如提示“下載完成”、“加載失敗”、隱藏進度條等等。

4 AsyncTask 示例

本節的功能和第 38 節 Handler 的功能類似,我們還是完成一個后臺執行耗時任務,并同步更新進度條的功能,可以直接在 Handler 的例子上修改。

4.1 布局文件

布局文件基本一樣,只是默認將進度條和進度顯示隱藏起來,等到onPreExecute()方法的時候在做初始化展示,代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ProgressBar
        android:visibility="gone"
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true" />

    <Button
        android:id="@+id/start_progress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/progressBar"
        android:layout_alignParentStart="true"
        android:layout_marginStart="24dp"
        android:layout_marginTop="62dp"
        android:text="開始任務" />

    <TextView
        android:visibility="gone"
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@+id/start_progress"
        android:layout_alignBottom="@+id/start_progress"
        android:layout_alignParentEnd="true"
        android:layout_marginEnd="85dp"
        android:gravity="center"
        android:text="當前進度:0%"
        android:textSize="16sp" />

</RelativeLayout>

4.2 異步任務控制

異步任務的代碼基本上和 Handler 一致,我們通過Thread.sleep(1000)來模擬一秒鐘的耗時操作,然后在onPreExecute()中展示進度條,在后臺任務執行完后通過publishProgress(int)來將任務進度發送到主線程,由onProgressUpdate(int)方法在 UI 線程接收進度,并更新進度條。最后在結束的時候返回“已完成”提示,在onPostExecute(String)方法中進行收尾工作,隱藏進度條并展示“已完成”。代碼如下:

package com.emercy.myapplication;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

public class MainActivity extends Activity {

    private static final int MAX = 100;

    private ProgressBar progressBar;
    private Button startProgress;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        progressBar = findViewById(R.id.progressBar);
        startProgress = findViewById(R.id.start_progress);
        textView = findViewById(R.id.textView);
        progressBar.setMax(MAX);

        startProgress.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new DownloadTask().execute();
            }
        });
    }

    // 1、創建Async Task子類
    private class DownloadTask extends AsyncTask<Integer, Integer, String> {

        // 2、初始化階段,展示進度條
        @Override
        protected void onPreExecute() {
            progressBar.setVisibility(View.VISIBLE);
            textView.setVisibility(View.VISIBLE);
        }

        // 3、執行后臺任務
        @Override
        protected String doInBackground(Integer... integers) {
            int i;
            for (i = 0; i < 100; i++) {
                try {
                    // 一秒鐘的耗時操作
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 4、發布進度
                publishProgress(i);
            }
            return "異步任務已完成";
        }

        // 5、接收后臺任務數據并更新進度條
        @Override
        protected void onProgressUpdate(Integer... values) {
            textView.setText("當前進度:" + values[0] + "%");
            progressBar.setProgress(values[0]);
        }

        // 6、任務結束
        @Override
        protected void onPostExecute(String s) {
            progressBar.setVisibility(View.GONE);
            textView.setText(s);
        }
    }
}

編譯之后,效果如下:

圖片描述

可以看到大體的效果和 Handler 是一樣的,但是整個異步任務的處理流程更加清晰了,而且可以很方便的做任務前的初始化任務后的回收工作,整個線程的切換也不再需要我們去控制,全部交給 AsyncTask 操作就行,一切就是這么簡單!

5 小結

本節介紹了一個專門用于執行異步任務的工具類,首先需要創建一個子類繼承自 AsyncTask,然后確定好需要傳入和返回的參數類型,接著覆寫 4 個關鍵的方法,剩下的線程切換及數據傳輸就交給系統完成了。
相比 Handler,AsyncTask使用更簡單,我們只需要關注我們的后臺邏輯,而 Handler 需要我們自行管理的東西比較多。所以在執行后臺任務刷新 UI 的場景,強烈推薦使用 AsyncTask,可以讓我們的開發更加順暢。