如何用它们的Rx等价替换AsyncTask和AsyncTaskLoader?

时间:2017-10-23 08:10:33

标签: android android-asynctask rx-android asynctaskloader

背景

有些文章声称Rx可以替代AsyncTask和AsyncTaskLoader。 看到Rx通常会缩短代码,我试着深入研究各种样本及其工作原理。

问题

我发现的所有样本和文章,包括Github repos,似乎都没有真正取代AsyncTask和AsyncTaskLoader:

  1. 无法找到如何取消任务或多个任务(包括中断线程)。这对AsyncTask尤为重要,AsyncTask通常用于RecyclerView和ListView。这也可以在AsyncTaskLoader上完成,但需要更多的工作而不仅仅是调用"取消"。对于AsyncTask,它还提供了一个进度的发布,这是我在Rx中找不到的另一个例子。

  2. 无法在避免内存泄漏的情况下找到如何避免手动取消订阅,这完全破坏了使用Rx的整个过程,因为它应该更短。在某些示例中,还有比普通代码更多的回调。

  3. 我尝试了什么

    以下是我读过有关Rx的一些链接:

    问题

    如何使用Rx替代AsyncTask和AsyncTaskLoader?

    例如,如何用Rx等效替换这个微小的AsyncTaskLoader示例代码:

    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)
            }
        }
    }
    

2 个答案:

答案 0 :(得分:0)

我先告诉你,我在RxJava的经历并不像有些人所说的那样。我只知道使用RxJava + RxAndroid替换AsyncTask。例如,让我们使用AsyncTask将您的Observable代码转换为等效的RxJava2,这可以说是最受欢迎的流类型。

Observable
    .create <Int> { emitter ->
        try { 
            for (i in 0..100) {
                Thread.sleep(5) 
                emitter.onNext(i) 
            }
        } catch (e: InterruptedException) {
            emitter.onError(e)
        }
        emitter.onComplete()
    ‎}
    ‎.subscribeOn(Schedulers.io())
    ‎.observeOn(AndroidSchedulers.mainThread())
    ‎.subscribe({ i ->
    ‎    holder.tv.text = "progress:" + i
     }, { e ->
     ‎   e.printStackTrace()
     ‎}, {
     ‎   holder.tv.text = "done:" + position
     ‎})

现在让我们逐行完成代码:

  1. create可让您自定义发射器行为。请注意,这不是创建流的唯一方法,还有justfromArrayfromListdefer等。我们可以讨论更多如果你还有兴趣的话。
  2. subscribeOn(Schedulers.io())告诉RxJava,#1将在I / O线程中执行。如果这是后台操作,Schedulers.newThread()也有效(至少据我所知)。
  3. observeOn(AndroidSchedulers.mainThread())告诉RxJava,#4将在Android主线程中执行。这也是RxAndroid填补空白的地方,因为RxJava没有任何Android的参考。
  4. subscribe()
    • onNext(i : Int) - 相当于publishProgress()。在较新的RxJava中,发出的值不能为空。
    • onError(e : Throwable) - 错误处理由RxJava的程序员长期赞美。在较新的RxJava中,必须提供onError(),如果它是空的,则无关紧要。
    • onComplete() - 相当于onPostExecute()
  5. 同样,我的回答可能会有很多错误信息。我希望有更好理解的人能够纠正它或提供更好的答案。

答案 1 :(得分:0)

似乎无法以更短或更简单的形式使用AsyncTask实现RxJava功能。使用RxJava可以简化任务链,因此如果只有一个任务AsyncTask可以是更好的解决方案,但是当您有几个连续的任务要做时 - RxJava的界面是更方便。因此,对于每个任务而不是例如程序onCancelled,您应该实现一些共同的逻辑。