Android 11 (R) 之 AsyncTask 前世今生

在过去的十年中,AsyncTask一直是用于在Android中编写并发代码的最广泛使用的解决方案之一
但是,从 Android 11 (R) 开始,官方正式弃用, 也标志着AsyncTask 的时代正式结束

本文我们一起来回顾它的前世今生


简介

async_task_deprecated.jpg

本名:AsyncTask
中文名:异步任务
出生日期:2009 年 Android 1.5 ( API Level 3 )
去世日期:2020 年 Android 11 ( API Level R )
所处位置android.os.AsyncTask

前世

作用

AsyncTask 是安卓开发中使用的一种轻量级异步任务类。其作用是在线程池中执行后台任务,并在执行过程中将执行进度传递给主线程,当任务执行完毕后,将最终结果传递给主线程。

产生背景

安卓系统线程分为主线程和子线程,主线程也叫UI线程。主线程主要负责与用户交互。为了更好的用户体验,保证系统不因主线程的阻塞而产生卡顿,安卓系统要求主线程中不能执行耗时任务。例如:IO操作、网络请求等必须在子线程中完成。AsyncTask就是为了适应这种需要而产生。

使用方式

直接使用 AsyncTask

1
2
3
AsyncTask.execute {
Log.d(TAG, "###### AsyncTask execute #####")
}

原型

— java

1
public abstract class AsyncTask < Params, Progress, Result >

— kotlin

1
abstract class AsyncTask<Params : Any!, Progress : Any!, Result : Any!>

AsyncTask是一个泛型抽象类

参数 说明
Params 执行后台任务所需参数类型
Progress 后台任务执行进度的类型
Result 台任务执行完毕后返回结果类型

并非所有类型都总是由异步任务使用。要将类型标记为未使用,只需使用该类型 Void

1
private class MyTask extends AsyncTask<Void, Void, Void> { ... }

核心方法

AsyncTask提供4个核心方法:

  1. protected void onPreExecute()

    参数 说明
    执行线程 主线程
    调用时间 异步任务执行之前
    方法作用 异步任务执行前的初始化工作
  2. protected Result doInBackground(Params…params)

    参数 说明
    执行线程 线程池中执行
    调用时间 任务开始后到任务结束之前
    方法作用 用于执行异步任务
  3. protected void onProgressUpdate(Prgress…values)

    参数 说明
    执行线程 主线程
    调用时间 任务开始后到任务结束之前
    方法作用 用于更新任务进度
  4. protected void onPostExecute(Result result)

    参数 说明
    执行线程 主线程
    调用时间 异步任务执行之后
    方法作用 将异步任务的执行结果传递给主线程

官方示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}

protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}

protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}

DownloadFilesTask类模拟文件下载过程。传入的参数Params类型为URL(文件地址),后台任务进程参数Progress类型为Integer(下载进度),后台任务返回结果参数Result类型为Long(总文件大小)。

创建完成后,任务将非常简单地执行:

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

取消任务

可以通过调用cancel(boolean)随时取消任务。 调用此方法将导致对isCancelled()的后续调用返回true。 调用此方法后,在doInBackground(java.lang.Object [])返回之后,将调用onCancelled(java.lang.Object)而不是onPostExecute(java.lang.Object)。 为了确保尽快取消任务,如果可能的话(例如在循环内),应始终定期从doInBackground(java.lang.Object [])检查isCancelled()的返回值。

使用注意

  1. AsyncTask类必须在主线程加载
  2. AsyncTask对象必须在主线程创建
  3. execute方法必须在主线程调用
  4. 不要在程序中直接调用AsyncTask提供的4个核心方法
  5. 一个AsyncTask对象只能执行一次,即只能调用一次execute

内存泄漏

问题描述

1
2
This AsyncTask class should be static or leaks might occur (anonymous android.os.AsyncTask) less... (Ctrl+F1) 
A static field will leak contexts. Non-static inner classes have an implicit reference to their outer class. If that outer class is for example a Fragment or Activity, then this reference means that the long-running handler/loader/task will hold a reference to the activity which prevents it from getting garbage collected. Similarly, direct field references to activities and fragments from these longer running instances can cause leaks. ViewModel classes should never point to Views or non-application Contexts

解决方法 : 静态内部类+弱引用

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
private static class MyTask extends AsyncTask<Void, Void, String> {

private WeakReference<MyActivity> activityReference;

//只保留一个弱引用到Activity
MyTask(MyActivity context) {
activityReference = new WeakReference<>(context);
}

@Override
protected String doInBackground(Void... params) {
return "task finished";
}

@Override
protected void onPostExecute(String result) {
//获取Activity的引用(如果仍存在)
MyActivity activity = activityReference.get();
if (activity == null || activity.isFinishing()) return;
// 验证Activity的UI
TextView textView = activity.findViewById(R.id.textview);
textView.setText(result);
// 访问Activity成员变量
activity.mSomeMemberVariable = 321;
}
}

今生

android_kotlin-1.jpg.png

abstract class AsyncTask < Params : Any!, Progress : Any!, Result : Any! >

这个类在API level R 被弃用
请使用标准的java.util.concurrentKotlin并发实用程序代替。

弃用原因

asynctask-r-aosp.png

AsyncTask旨在启用和轻松使用UI线程。但是,最常见的用例是集成到UI中,这会导致Context泄漏,丢失的回调或配置更改崩溃。它还在平台的不同版本上具有不一致的行为,吞噬的异常doInBackground,并且不能提供比Executor直接使用更多的实用程序。

AsyncTask是为围绕ThreadHandler的帮助器类而被设计出来的,并且不构成通用的线程框架。理想情况下,应将AsyncTasks用于较短的操作(最多几秒钟)。如果需要使线程长时间运行,则强烈建议您使用java.util.concurrent包提供的各种API,例如 ExecutorThreadPoolExecutorFutureTask

代替示例

Executor

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
// ----------- 示例1 ----------------------
//定义
class DirectExecutor : Executor {
override fun execute(runnable: Runnable?) {
runnable?.run()
}
}
//调用
val directExecutor = DirectExecutor()
directExecutor.execute {
Log.d(TAG, "###### DirectExecutor execute ##### ")
.........
.........
}

//----------- 示例2 ----------------------
//定义
class ThreadPerTaskExecutor : Executor {
override fun execute(runnable: Runnable?) {
Thread(runnable).start()
}
}
//调用
val threadPerTaskExecutor = ThreadPerTaskExecutor()
threadPerTaskExecutor.execute {
Log.d(TAG, "###### threadPerTaskExecutor execute #####")
.........
.........
}

ThreadPoolExecutor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//核心线程池大小 --> 即需要开启多少个线程
val coreThreadPoolSize = Runtime.getRuntime().availableProcessors() //系统有多少可用就开多少
//最大线程池大小
val maximumPoolSize = 15
//线程最大空闲时间
//线程池中超过corePoolSize数目的空闲线程最大存活时间;可以allowCoreThreadTimeOut(true)使得核心线程 有效时间
val keepAliveTime = 120L
//keepAliveTime时间单位
val unit = TimeUnit.SECONDS
//定义阻塞式列队
val workQueue = ArrayBlockingQueue<Runnable>(10000)
val executor = ThreadPoolExecutor(coreThreadPoolSize,
maximumPoolSize, keepAliveTime, unit, workQueue
)
for (i in 0..100) {
executor.execute {
Log.d(TAG, "线程池中线程数目:"+executor.poolSize +",队列中等待执行的任务数目:"+
executor.queue.size +",已执行完成的任务数目:"+executor.completedTaskCount)
}
}
executor.shutdown()

FutureTask

1
2
3
4
val service = Executors.newSingleThreadExecutor()
val futureTask = FutureTask(Callable { ......... })
service.execute(futureTask)
Log.d(TAG, "###### futureTask execute ##### ${futureTask.get()}")

Kotlin并发(协程)实用程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//定义耗时操作的方法
private fun spendLongTimeAction() : String{
//模拟网络请求,耗时5s
Thread.sleep(5000)
return "This is a json data from web server"
}
//调用
GlobalScope.launch {
//使用给定的协程上下文调用指定的暂停块,暂停直到完成,然后返回结果。
withContext(Dispatchers.Main){
val data = spendLongTimeAction()
textView.text = data
Toast.makeText(this@MainActivity,"数据请求成功",Toast.LENGTH_LONG).show()
}
}

相关文献

  1. API Differences between 29 and rdp1
  2. AsyncTask is Deprecated, Now What?
  3. Deprecating AsyncTask in Android with Kotlin Coroutines
  4. Google depreciated AsyncTask from Android 11

以上就是本文对AsyncTask的简单回顾总结

本文发布于Android 11 / R DP 1 。如有不足,欢迎指正

0%