Android 中已弃用的 AsyncTask 的替代方案
Android 中的AsyncTask(异步任务)是一个抽象类,或者更确切地说是一个帮助类,它让应用程序在后台执行繁琐的任务并在前端并行执行 UI 更改。这与 Threading 非常相似,但不构成任何线程框架。可以使用 AsyncTask 执行加载图像、下载音乐、图片、文件等后台任务。但是,现在不推荐使用 AsyncTask,开发人员可能迟早需要为此提供替代方案。
通过本文,我们将向您展示 2 种方法,通过它们您可以执行后台任务并同时更新 UI 元素。两种选择是:
- 使用 Executor 和 Handler 的组合
- 使用线程
但首先,让我们看看 AsyncTask 是如何工作的。
AsyncTask 通用代码结构
下面的代码实现了一个AsyncTask到一个内部类MyCustomClass 。 AsyncTask 需要三个参数:
- 参数:可以是 Int、String 类型,可以是任何作为输入的对象,用于执行假定的任务,当没有输入时为 Void。
- 进度:执行任务时发布的单位,不需要时作废。
- 结果:可以是 Int、String 类型,任务完成后的任何对象。当不需要输出变量时无效。
关联的成员函数有:
- onPreExecute()可选
- doInBackground(Params...)必需
- onProgressUpdate(Progress…)可选
- onPostExecute(Result)可选
下面是实现AsyncTask的基本代码结构。
Kotlin
import android.os.AsyncTask
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
// AsyncTask ()
inner class MyCustomClass: AsyncTask(){
override fun doInBackground(vararg params: String?): String {
TODO()
}
override fun onPostExecute(result: String?) {
TODO()
}
}
}
Kotlin
import android.annotation.SuppressLint
import android.os.AsyncTask
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val myTextView = findViewById(R.id.textView)
val myInput = 100
MyCustomClass(myTextView).execute(myInput)
}
// Input type is Int and Result is a String
@SuppressLint("StaticFieldLeak")
inner class MyCustomClass(var textView: TextView): AsyncTask(){
override fun doInBackground(vararg params: Int?): String {
// Convert the input Params:Int
// to String and return the Result:String
return params[0].toString()
}
// Result:String is set as text in the passed TextView
override fun onPostExecute(result: String?) {
textView.text = result
}
}
}
XML
Kotlin
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import java.util.concurrent.Executors
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val myExecutor = Executors.newSingleThreadExecutor()
val myHandler = Handler(Looper.getMainLooper())
myExecutor.execute {
// Do something in background (back-end process)
}
myHandler.post {
// Do something in UI (front-end process)
}
}
}
Kotlin
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.widget.TextView
import java.util.concurrent.Executors
class MainActivity : AppCompatActivity() {
private val myExecutor = Executors.newSingleThreadExecutor()
private val myHandler = Handler(Looper.getMainLooper())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val myTextView = findViewById(R.id.textView)
val myInput = 55
doMyTask(myTextView, myInput)
}
private fun doMyTask(textView: TextView, input: Int){
myExecutor.execute {
val result = input.toString()
myHandler.post {
textView.text = result
}
}
}
}
XML
Kotlin
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Thread(Runnable {
// Do something in Background (Back-end)
runOnUiThread {
// Do something in UI (Front-end)
}
}).start()
}
}
Kotlin
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val myTextView = findViewById(R.id.textView)
val myInput = 26
doMyTask(myTextView, myInput)
}
private fun doMyTask(textView: TextView, input: Int){
Thread(Runnable {
val result = input.toString()
runOnUiThread {
textView.text = result
}
}).start()
}
}
XML
例子:
现在在这个例子中,我们将一个整数值(输入)传递给任务。在后台将输入转换为String,转换后会显示在TextView中。
科特林
import android.annotation.SuppressLint
import android.os.AsyncTask
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val myTextView = findViewById(R.id.textView)
val myInput = 100
MyCustomClass(myTextView).execute(myInput)
}
// Input type is Int and Result is a String
@SuppressLint("StaticFieldLeak")
inner class MyCustomClass(var textView: TextView): AsyncTask(){
override fun doInBackground(vararg params: Int?): String {
// Convert the input Params:Int
// to String and return the Result:String
return params[0].toString()
}
// Result:String is set as text in the passed TextView
override fun onPostExecute(result: String?) {
textView.text = result
}
}
}
XML
输出:
所以基本上,代码工作得很好。但是,我们已经使用AsyncTask来执行我们的任务,它已被弃用,迟早需要替换。
备选方案 1:使用 Executor 和 Handler
下面有一个示例代码。如果您看到,则声明了一个执行程序和一个处理程序。执行器将帮助在后台执行任何任务,处理程序将帮助进行 UI 更改。
科特林
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import java.util.concurrent.Executors
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val myExecutor = Executors.newSingleThreadExecutor()
val myHandler = Handler(Looper.getMainLooper())
myExecutor.execute {
// Do something in background (back-end process)
}
myHandler.post {
// Do something in UI (front-end process)
}
}
}
例子:
我们使用了与AsyncTask相同的示例。在这里,执行程序将执行任务,一旦获得所需的结果,就会使用处理程序更改 UI。
科特林
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.widget.TextView
import java.util.concurrent.Executors
class MainActivity : AppCompatActivity() {
private val myExecutor = Executors.newSingleThreadExecutor()
private val myHandler = Handler(Looper.getMainLooper())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val myTextView = findViewById(R.id.textView)
val myInput = 55
doMyTask(myTextView, myInput)
}
private fun doMyTask(textView: TextView, input: Int){
myExecutor.execute {
val result = input.toString()
myHandler.post {
textView.text = result
}
}
}
}
XML
输出:
代码运行得很好。我们得到了想要的结果。
备选方案 2:使用线程
下面的代码运行一个线程。标记的注释显示了要进行背景和 UI 更改的代码的位置。使用线程时,任何 UI 更改都必须在 UI 线程内运行,任何后台进程都必须在 UI 线程外运行。
科特林
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Thread(Runnable {
// Do something in Background (Back-end)
runOnUiThread {
// Do something in UI (Front-end)
}
}).start()
}
}
例子:
现在让我们看一个示例,我们将传递一个 Integer 以将其转换为 String 并将其传递给 TextView。
科特林
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val myTextView = findViewById(R.id.textView)
val myInput = 26
doMyTask(myTextView, myInput)
}
private fun doMyTask(textView: TextView, input: Int){
Thread(Runnable {
val result = input.toString()
runOnUiThread {
textView.text = result
}
}).start()
}
}
XML
输出:
代码运行得很好。我们有我们想要的结果。