AsyncTask介绍及基本用法

195 阅读5分钟

在Android开发中,异步操作是必不可少的。我们经常需要在后台线程中执行耗时操作,比如网络请求、文件读写等,同时保持用户界面的响应速度。为了解决这个问题,Android提供了AsyncTask类,方便开发者在后台线程执行任务,并将结果传递给主线程以更新UI。

AsyncTask是一个抽象类,它简化了在后台线程执行任务并将结果传递回主线程的过程。尽管随着时间的推移,AsyncTask的使用逐渐被RxJavaKotlin Coroutines等更强大的工具取代,但它依然是理解异步编程的重要一环。

1. AsyncTask的基本结构

一个AsyncTask通常包含以下三个泛型参数:

  • Params:传递给异步任务执行时的参数类型。
  • Progress:后台任务执行过程中,进度的类型。
  • Result:后台任务执行完毕后,返回结果的类型。

AsyncTask主要包含以下几个方法:

  1. onPreExecute():在异步任务开始之前在主线程中调用,用于进行初始化操作。
  2. doInBackground(Params... params):在后台线程中执行异步任务,必须重写该方法。所有耗时操作都应该在此进行。
  3. onProgressUpdate(Progress... values):在主线程中调用,用于更新任务进度。
  4. onPostExecute(Result result):在异步任务完成后在主线程中调用,用于处理任务结果。
  5. onCancelled():当任务被取消时在主线程中调用。

2. 基本用法

下面是一个简单的AsyncTask示例,用于演示如何在后台线程中执行任务,并在任务完成后更新UI。

示例:模拟一个耗时任务

import android.os.AsyncTask;
import android.widget.TextView;

// 定义一个AsyncTask子类
public class MyAsyncTask extends AsyncTask<Void, Integer, String> {
    private TextView textView;

    // 构造函数,传入TextView用于更新UI
    public MyAsyncTask(TextView textView) {
        this.textView = textView;
    }

    // 在主线程中执行,进行初始化操作
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        textView.setText("Task Starting...");
    }

    // 在后台线程中执行耗时操作
    @Override
    protected String doInBackground(Void... voids) {
        for (int i = 0; i <= 100; i += 20) {
            try {
                Thread.sleep(1000); // 模拟耗时操作
                publishProgress(i); // 更新进度
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return "Task Completed";
    }

    // 在主线程中执行,更新任务进度
    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        textView.setText("Progress: " + values[0] + "%");
    }

    // 在主线程中执行,处理任务结果
    @Override
    protected void onPostExecute(String result) {
        super.onPostExecute(result);
        textView.setText(result);
    }

    // 当任务被取消时调用
    @Override
    protected void onCancelled() {
        super.onCancelled();
        textView.setText("Task Cancelled");
    }
}

使用AsyncTask

在Activity中使用AsyncTask,示例如下:

import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    private TextView textView;
    private MyAsyncTask myAsyncTask;

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

        textView = findViewById(R.id.textView);
        myAsyncTask = new MyAsyncTask(textView);
        myAsyncTask.execute(); // 执行AsyncTask
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (myAsyncTask != null && myAsyncTask.getStatus() == AsyncTask.Status.RUNNING) {
            myAsyncTask.cancel(true); // Activity销毁时取消任务
        }
    }
}

在上述示例中,MyAsyncTask在后台线程中模拟了一个耗时任务,并通过publishProgress方法将进度更新到主线程。在任务完成后,通过onPostExecute方法将结果显示在TextView上。

注意事项

  1. 内存泄漏AsyncTask可能会导致内存泄漏,因为它持有对ActivityFragment的引用。为避免内存泄漏,可以使用静态内部类,并通过弱引用(WeakReference)持有ActivityFragment的引用。
  2. 任务取消:在ActivityFragment销毁时,应该取消未完成的AsyncTask,避免不必要的资源浪费。
  3. 多任务并行AsyncTask默认使用一个单线程的线程池,多个任务会按顺序执行。如果需要并行执行多个任务,可以使用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)方法。

3. 工作流程

1. 创建与执行

当一个AsyncTask对象被创建并调用execute(Params... params)方法时,任务依次经历以下阶段:

  1. 初始化

    • 调用executeOnExecutor(Executor exec, Params... params)方法(execute(Params... params)是调用此方法的一种方式,默认使用串行执行器SERIAL_EXECUTOR)。
    • 初始化任务的状态
    • 在主线程中调用onPreExecute()方法,用于进行初始化操作,如显示进度条等。
  2. 通过 execute 方法将任务提交到线程池

    • AsyncTask内部维护了一个线程池(默认核心线程数为5,最大线程数为128)。
    • executeOnExecutor方法中会调用线程池的execute方法来执行任务。
  3. 后台任务执行

    • 在线程池中执行doInBackground(Params... params)方法,所有耗时操作在此进行。
  4. 进度更新

    • doInBackground中调用publishProgress(Progress... values)方法。
    • publishProgress方法内部通过Handler将消息发送到主线程,触发onProgressUpdate方法以更新进度。
  5. 任务完成

    • doInBackground执行完毕后,通过Handler将结果传递给主线程,并调用onPostExecute(Result result)方法,处理任务结果。

2. 线程池与Handler

AsyncTask内部依赖于ExecutorHandler实现异步任务的调度和执行。

  • ExecutorAsyncTask默认使用一个串行执行器(SERIAL_EXECUTOR),该执行器内部维护了一个队列,任务将按顺序执行。开发者也可以通过executeOnExecutor(Executor exec, Params... params)方法指定并行执行器(THREAD_POOL_EXECUTOR)以实现并发执行。

  • HandlerAsyncTask内部使用一个静态HandlerInternalHandler)来切换线程并处理消息。Handler用于将后台线程中的消息传递到主线程,以执行onProgressUpdateonPostExecute等方法。

3. 任务取消

当调用cancel(boolean mayInterruptIfRunning)方法时,AsyncTask会尝试取消正在执行的任务。

  • 标记取消状态:设置任务的取消标记。
  • 线程中断:如果mayInterruptIfRunningtrue,将尝试中断正在执行的线程。
  • 回调onCancelled方法:任务取消后,将调用onCancelled(Result result)onCancelled()方法,允许开发者处理取消操作。

代码示例

下面是一个简化版的AsyncTask实现,用于演示其内部工作原理。

import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public abstract class SimpleAsyncTask<Params, Progress, Result> {
    private static final Executor THREAD_POOL_EXECUTOR = Executors.newFixedThreadPool(5);
    private static final Handler HANDLER = new Handler(Looper.getMainLooper());

    private boolean isCancelled = false;

    protected abstract void onPreExecute();
    protected abstract Result doInBackground(Params... params);
    protected abstract void onPostExecute(Result result);
    protected abstract void onProgressUpdate(Progress... values);
    protected void onCancelled(Result result) {
        onCancelled();
    }
    protected void onCancelled() {}

    public final void execute(Params... params) {
        onPreExecute();
        THREAD_POOL_EXECUTOR.execute(() -> {
            Result result = doInBackground(params);
            HANDLER.post(() -> {
                if (isCancelled) {
                    onCancelled(result);
                } else {
                    onPostExecute(result);
                }
            });
        });
    }

    public final void publishProgress(Progress... values) {
        HANDLER.post(() -> onProgressUpdate(values));
    }

    public final void cancel(boolean mayInterruptIfRunning) {
        isCancelled = true;
        // Note: Thread interruption is not handled in this simplified version
    }

    public final boolean isCancelled() {
        return isCancelled;
    }
}

总结

尽管AsyncTask在现代Android开发中不再是首选,但它依然是理解Android异步编程的基础工具。掌握AsyncTask的使用和工作流程,有助于更好地理解和使用更高级的异步编程工具。