AsyncTask 用于将耗时操作放到后台线程执行,同时在任务开始和结束时在主线程更新 UI。本文将逐步讲解其基本概念、生命周期方法,并提供一个简单的示例代码。
值得注意的是, 从 Android 11 开始,AsyncTask 已被弃用,建议在新项目中使用 Kotlin 协程、WorkManager 或其他异步方案,本文仅做学习记录使用,而且第一行代码P396 10.2.4讲的更详细更好。
1. AsyncTask 的基本概念
AsyncTask 使用三个泛型参数来确定任务的输入、进度和结果类型:
- Params:传递给任务的参数类型。
- Progress:任务执行中更新进度时使用的数据类型。
- Result:任务执行完毕后返回的结果类型。
例如:
plaintext
1 | class MyTask : AsyncTask<Void, Int, String>() { ... } |
表示该任务不需要输入参数(Void)、进度为 Int 类型、最终结果为 String。
2. AsyncTask 的生命周期方法
AsyncTask 定义了多个回调方法,每个方法在任务执行的不同阶段被调用:
- onPreExecute()
在任务开始前调用,通常用于初始化 UI(例如显示进度条)。 - doInBackground(vararg params: Params)
在后台线程中执行耗时操作。此方法不能直接更新 UI,但可以调用publishProgress()
触发进度更新。 - onProgressUpdate(vararg values: Progress)
当调用publishProgress()
时在主线程中执行,用于更新 UI 中的进度显示。 - onPostExecute(result: Result)
在后台任务执行完毕后调用,运行在主线程中,可以使用任务返回的结果更新 UI。 - onCancelled()
当任务被取消时调用,可用于清理操作。
3. Kotlin 示例代码
下面的示例展示了如何使用 AsyncTask 在后台模拟一个耗时任务(例如计数操作),并在 UI 上更新进度和显示结果。
plaintext
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 | import android.os.AsyncTask import android.os.Bundle import android.widget.ProgressBar import android.widget.TextView import androidx.appcompat.app.AppCompatActivity class MainActivity : AppCompatActivity() { private lateinit var progressBar: ProgressBar private lateinit var textView: TextView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) progressBar = findViewById(R.id.progressBar) textView = findViewById(R.id.textView) // 传入一个整数参数,这个参数会被传递到doInBackground()方法当中,例如:10 MyTask().execute(10) } // MyTask 类继承自 AsyncTask<Int, Int, String> private inner class MyTask : AsyncTask<Int, Int, String>() { // 在任务开始前调用,通常用于初始化界面,比如显示进度条 override fun onPreExecute() { super.onPreExecute() progressBar.progress = 0 textView.text = "任务开始..." } // 在后台线程中执行耗时操作 // params 数组中包含 execute() 方法传入的参数,这里我们只传入一个 Int 类型的参数 override fun doInBackground(vararg params: Int?): String { // 获取传入的参数值,假设它表示任务总步数 val totalSteps = params.firstOrNull() ?: 0 for (i in 1..totalSteps) { try { // 模拟耗时操作,例如睡眠 1 秒 Thread.sleep(1000) } catch (e: InterruptedException) { e.printStackTrace() } // 调用 publishProgress() 更新进度,触发 onProgressUpdate() publishProgress(i) } // 返回任务完成后的结果 return "任务完成,共计 $totalSteps 秒" } // 在主线程中接收并更新任务进度 override fun onProgressUpdate(vararg values: Int?) { super.onProgressUpdate(*values) // 更新 ProgressBar 和 TextView 显示当前进度 values.firstOrNull()?.let { progress -> progressBar.progress = progress * 100 / (intent.getIntExtra("TOTAL_STEPS", 10)) textView.text = "当前进度: $progress/${intent.getIntExtra("TOTAL_STEPS", 10)}" } } // 在任务执行完毕后调用,传入 doInBackground() 返回的结果 override fun onPostExecute(result: String?) { super.onPostExecute(result) textView.text = result } // 可选:任务取消时调用 override fun onCancelled() { super.onCancelled() textView.text = "任务取消" } } } |
代码说明
- onPreExecute()
初始化 ProgressBar 和 TextView 的显示状态。 - doInBackground()
在后台执行一个简单的循环操作,每次循环暂停 50 毫秒,并调用publishProgress()
更新 UI 的进度。 - onProgressUpdate()
接收到进度后,在主线程更新 ProgressBar 和 TextView。 - onPostExecute()
当任务执行完毕后,显示完成消息。
4. 注意事项与最佳实践
- 内存泄漏问题
如果 AsyncTask 是作为 Activity 的内部类存在,当 Activity 被销毁时可能导致内存泄漏。建议将 AsyncTask 定义为静态内部类,并通过 WeakReference 持有 Activity 的引用。 - 执行策略
在 API 11(Android 3.0)及以上版本,AsyncTask 默认串行执行。如果需要并行执行,可以使用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
。 - 已弃用提醒
从 Android 11 开始,AsyncTask 已被弃用,建议在新项目中使用 Kotlin 协程、WorkManager 或其他异步方案。
5. 为什么推荐 Kotlin 协程?
- 代码简洁
Kotlin 协程可以让异步代码写得像同步代码,减少回调层级和代码冗余。 - 更好的错误处理与取消机制
协程内置了结构化并发,便于管理任务生命周期和异常处理。 - 官方支持
谷歌和 JetBrains 都推荐在 Android 项目中使用 Kotlin 协程来处理异步任务。
6. 总结
本文详细介绍了在 Kotlin 中使用 AsyncTask 的方法,包括各个生命周期回调的作用和示例代码。虽然 AsyncTask 曾经为 Android 异步处理提供了简化方案,但随着技术的发展,Kotlin 协程已成为更推荐的选择。在学习 AsyncTask 的同时,建议大家尽快熟悉并尝试使用 Kotlin 协程以提升代码的简洁性和可维护性。
Author: GoshenC