Background
Some articles claim that Rx can replace AsyncTask and AsyncTaskLoader. Seeing that Rx usually makes code shorter, I tried to dive into various samples and ideas of how it works.
The problem
All of the samples and articles I've found, including Github repos, don't seem to really replace AsyncTask and AsyncTaskLoader well:
Can't find how to cancel a task or multiple ones (including interrupting a thread). This is especially important for AsyncTask, which is usually used for RecyclerView and ListView. This can also be done on AsyncTaskLoader, but requires a bit more work than just call "cancel". For AsyncTask, it also provides publishing of a progress, and that's another thing I can't find an example of in Rx.
Can't find how to avoid unsubscribing manually while avoiding memory leaks, which quite ruins the whole point of using Rx, as it's supposed to be shorter. In some samples, there are even more callbacks than normal code has.
What I've tried
Here are some links I've read about Rx:
https://stablekernel.com/replace-asynctask-and-asynctaskloader-with-rx-observable-rxjava-android-patterns/ - this actually provides a cool way to handle AsyncTaskLoader, as it caches the result. However, the samples don't work at all (including building them), as they are very old.
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 - all those talk mostly about RxJava, and not about replacement of AsyncTask or AsyncTaskLoader.
https://github.com/L4Digital/RxLoader - requires unsubscribing, even though it doesn't say it needs it.
The question
How can I use Rx as replacement for AsyncTask and AsyncTaskLoader?
For example, how would I replace this tiny AsyncTaskLoader sample code with Rx equivalent:
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
}
}
}
For AsyncTask, usually we set it per ViewHolder, as a field there, and we cancel it upon onBindViewHolder, and when activity/fragment gets destroyed (go over all those that still pending or running).
Something like this (sample made as short as possible, of course it should be changed depending on needs) :
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)
}
}
}