全国协议5人面授小班,企业级独立开发考核,转业者的IT软件工程师基地 登录/注册 | 如何报名
当前位置: Java   >  AsyncTask:异步任务
admin · 更新于 2021-08-06

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
        }
    }
代码块
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

以上是 AsyncTask 的核心回调方法,每个方法的含义会在后面具体讲到。

2.2 指定参数

AsyncTask 可以帮助你在子线程和主线程之间同步参数,根据不同的业务场景,参数类型和个数也会不一样。刚刚在 2.1 小节声明 AyncTask 子类的时候,需要传入 3 个泛型参数:

  • TypeOfVarArgParams:
    在任务启动之后,传入给后台任务的参数类型

  • ProgressValue:
    启动之后到任务结束之间,系统会不断回调此方法,用来更新任务的进度

  • ** ResultValue:**
    后台任务的执行结果

2.3 启动后台任务

在声明完后台任务之后,就可以直接启动了。启动方式比较简单,直接通过调用execute()方法启动后台任务:

new DownloadFilesTask().execute(url1, url2, url3);
代码块
  • 1

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>
代码块
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

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);
        }
    }}
代码块
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78

编译之后,效果如下:

可以看到大体的效果和 Handler 是一样的,但是整个异步任务的处理流程更加清晰了,而且可以很方便的做任务前的初始化任务后的回收工作,整个线程的切换也不再需要我们去控制,全部交给 AsyncTask 操作就行,一切就是这么简单!

5 小结

本节介绍了一个专门用于执行异步任务的工具类,首先需要创建一个子类继承自 AsyncTask,然后确定好需要传入和返回的参数类型,接着覆写 4 个关键的方法,剩下的线程切换及数据传输就交给系统完成了。
相比 Handler,AsyncTask使用更简单,我们只需要关注我们的后台逻辑,而 Handler 需要我们自行管理的东西比较多。所以在执行后台任务刷新 UI 的场景,强烈推荐使用 AsyncTask,可以让我们的开发更加顺畅。


为什么选择汉码未来