What is the correct way of using Anko Coroutines extensions?

孤街浪徒 提交于 2019-12-05 07:15:34

问题


So I am migrating an example app from RxJava to Kotlin/Anko Corountines and I am wondering if I am doing the best (first) approach of it:

fun getPopulationList() {
    val ref = asReference()

    async(UI) {
        try {
            ref().setCurrentState(ViewState.State.LOADING)
            val background = bg {
                repository.populationResponse().execute().body()
            }

            ref().let {
                it.response = background.await()
                it.mvpView?.onGetData(it.response)
                it.setCurrentState(ViewState.State.FINISH)
            }
        } catch (e: Exception) {
            e.printStackTrace()
            ref().mvpView?.onError(e)
        }
    }
}

I am using an MVP architecture where my Presenter base class had a CompositeSubscription and in the onDestroy's fragment or activity method simple unsubscribe and clear the CompositeSubscription object. But i am wondering if the asReference() function from Anko Coroutines does the same and there is no need to save a list of Deferred<T> and then iterate it and cancel one by one.

BTW if I add a Thread.sleep(5000) to simulate a big transaction and destroy the Fragment I can see in the logcat the HTTP response even after the fragment is not visible/destroyed while with RxJava doesn't happen, so I think I am not using properly.

UPDATE

 fun getPopulationList() {
    val ref = asReference()

    job = launch(UI) {

        try {
            ref().setCurrentState(ViewState.LOADING)
            val background = bg {
                Thread.sleep(5000) //simulate heavy IO

                if (isActive) {
                    repository.populationResponse().execute().body()
                } else {
                    return@bg null
                }
            }

            ref().let {
                it.response = background.await()
                it.mvpView?.onGetData(it.response)
                it.setCurrentState(ViewState.FINISH)
            }
        } catch (e: Exception) {
            RestHttpExceptionHandler().handle(UI, e, ref())
        }
    }
}

I am able to cancel the coroutine while calling job.cancel() in onDestroy() method but to make it work I have to check if the job is active or not and that translate into an if/else and a return or not data. Is there any better way to return something when the job was cancelled?


回答1:


As you can see in asReference() source it is nothing other than a weak reference and invoke method to get reference that throws CancellationException when object is collected. it does not do anything to cancel operation. just is aware of collected object.

so you need to keep reference of a Job or subtype of it to cancel operation.

launch coroutine builder from kotlinx.coroutines returns a Job instance. here is an example:

private lateinit var job: Job

private fun startCoroutines() {
    val ref = asReference()
    job = launch(UI) {
        try {
            val deferred = async(parent = coroutineContext[Job]) {
                    //do some work
                    result//return
            }

            ref().setData(deferred.await())
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

}


override fun onDestroy() {
    super.onDestroy()
    job.cancel()
}

Notes:

1- when result type is not important launch can be used instead of async.

2- to do cancellation in child coroutines you must create parent/child job hierarchies. I passed parent(launch) Job reference to child coroutine(async) to achieve this.

3- as cancellation is cooperative cancellation implementation must be done in async(see examples here).

3- job.cancel() is used in onDestroy cancels job and it's child async. this can be done in Presenter in MVP pattern.



来源:https://stackoverflow.com/questions/47461766/what-is-the-correct-way-of-using-anko-coroutines-extensions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!