【发布时间】:2018-04-03 17:21:32
【问题描述】:
背景
有些文章声称 Rx 可以替代 AsyncTask 和 AsyncTaskLoader。 看到 Rx 通常会使代码更短,我尝试深入研究各种示例和有关其工作原理的想法。
问题
我发现的所有示例和文章,包括 Github 存储库,似乎都不能很好地替代 AsyncTask 和 AsyncTaskLoader:
找不到如何取消一个或多个任务(包括中断线程)。这对于 AsyncTask 尤为重要,通常用于 RecyclerView 和 ListView。这也可以在 AsyncTaskLoader 上完成,但需要比仅仅调用“取消”更多的工作。对于 AsyncTask,它还提供进度的发布,这是我在 Rx 中找不到示例的另一件事。
找不到如何在避免内存泄漏的同时避免手动取消订阅,这完全破坏了使用 Rx 的全部意义,因为它应该更短。在某些示例中,回调的数量甚至比普通代码还要多。
我尝试过的
这是我读过的一些关于 Rx 的链接:
https://stablekernel.com/replace-asynctask-and-asynctaskloader-with-rx-observable-rxjava-android-patterns/ - 这实际上提供了一种很酷的方式来处理 AsyncTaskLoader,因为它缓存了结果。但是,这些示例根本不起作用(包括构建它们),因为它们非常旧。
https://code.tutsplus.com/tutorials/getting-started-with-rxjava-20-for-android--cms-28345、https://code.tutsplus.com/tutorials/reactive-programming-operators-in-rxjava-20--cms-28396、https://code.tutsplus.com/tutorials/rxjava-for-android-apps-introducing-rxbinding-and-rxlifecycle--cms-28565 - 所有这些都主要谈论 RxJava,而不是关于替换 AsyncTask 或 AsyncTaskLoader。
https://github.com/L4Digital/RxLoader - 需要取消订阅,即使它没有说它需要它。
问题
如何使用 Rx 替代 AsyncTask 和 AsyncTaskLoader?
例如,我如何将这个微小的 AsyncTaskLoader 示例代码替换为 Rx 等效代码:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
supportLoaderManager.initLoader(1, Bundle.EMPTY, object : LoaderCallbacks<Int> {
override fun onCreateLoader(id: Int, args: Bundle): Loader<Int> {
Log.d("AppLog", "onCreateLoader")
return MyLoader(this@MainActivity)
}
override fun onLoadFinished(loader: Loader<Int>, data: Int?) {
Log.d("AppLog", "done:" + data!!)
}
override fun onLoaderReset(loader: Loader<Int>) {}
})
}
private class MyLoader(context: Context) : AsyncTaskLoader<Int>(context) {
override fun onStartLoading() {
super.onStartLoading()
forceLoad()
}
override fun loadInBackground(): Int {
Log.d("AppLog", "loadInBackground")
try {
Thread.sleep(10000)
} catch (e: InterruptedException) {
e.printStackTrace()
}
return 123
}
}
}
对于 AsyncTask,通常我们为每个 ViewHolder 设置它,作为一个字段,然后在 onBindViewHolder 以及活动/片段被销毁时取消它(检查所有仍处于挂起或运行的状态)。
类似这样的东西(样本尽可能短,当然应该根据需要进行更改):
class AsyncTaskActivity : AppCompatActivity() {
internal val mTasks = HashSet<AsyncTask<Void, Int, Void>>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_async_task)
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
recyclerView.adapter = object : Adapter<MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(LayoutInflater.from(this@AsyncTaskActivity).inflate(android.R.layout.simple_list_item_1, parent, false))
}
@SuppressLint("StaticFieldLeak")
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.tv.text = "loading..."
if (holder.task != null) {
holder.task!!.cancel(true)
mTasks.remove(holder.task!!)
}
holder.task = object : AsyncTask<Void, Int, Void>() {
override fun doInBackground(vararg voids: Void): Void? {
for (i in 0..100)
try {
Thread.sleep(5)
publishProgress(i)
} catch (e: InterruptedException) {
e.printStackTrace()
}
return null
}
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
holder.tv.text = "progress:" + values[0]
}
override fun onPostExecute(aVoid: Void?) {
super.onPostExecute(aVoid)
mTasks.remove(holder.task)
holder.tv.text = "done:" + position
holder.task = null
}
}.execute()
}
override fun getItemCount(): Int {
return 1000
}
}
}
override fun onDestroy() {
super.onDestroy()
for (task in mTasks)
task.cancel(true)
}
private class MyViewHolder(itemView: View) : ViewHolder(itemView) {
internal var tv: TextView
internal var task: AsyncTask<Void, Int, Void>? = null
init {
tv = itemView.findViewById(android.R.id.text1)
}
}
}
【问题讨论】:
-
关于问题 #1:在 RxJava2 中,对于每个订阅,您都会得到一个
Disposable接口,该接口具有dispose()函数。我相信这相当于AsyncTask的cancel() -
关于问题#2:我通常将那些
Disposables 的引用保存在集合中,dispose()每个都保存在onDestroy()中,或者onDestroyView()中的片段中。如果这个评论没有意义,那么我显然不明白这个的前提。 -
@HendraAnggrian “处置”不仅仅忽略结果吗?它是否允许也中断线程?您能否发布一个与我所做的大致相同的示例代码? AsyncTask 还支持发布进度。 Rx 也支持吗?
-
我对 RxJava 的体验只包含 3 种流类型:
Observable、Single和Completable。如果要发布进度,则需要Observable,它的onNext()相当于AsyncTask的publishProgress()。我会编写一个示例 RxJava+RxAndroid 代码来模仿您的AsyncTask代码,但我发现您的代码中没有publishProgress()。在这种情况下,Single或Completable可能更可取,因为它们更简单。编辑:我目前在外面,将在一个小时左右发布代码示例。 -
我可以轻松添加它。只是我现在想起来了,所以我也添加了它。我现在在问题中更新了 AsyncTask 示例的代码。
标签: android android-asynctask rx-android asynctaskloader