如何取消正在运行的LiveData协程块

时间:2019-08-30 09:05:28

标签: android kotlin android-livedata kotlin-coroutines kotlin-extension

通过使用LiveData的最新版本“ androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-alpha03”,我使用LiveData的新构件(LiveData +协程(Coroutine),使用Retrofit执行同步网络调用,并相应地更新ViewModel中的不同标志(isLoading,isError)。我在“查询” LiveData上使用Transforamtions.switchMap,因此只要UI中的“查询”发生更改,“搜索产品”代码便会使用Transformations.switchMap开始执行。一切正常,除了“查询” LiveData中发生更改时我想取消以前的“改装呼叫”。目前,我看不到任何方法。任何帮助,将不胜感激。

class ProductSearchViewModel : ViewModel() {
    val completableJob = Job()
    private val coroutineScope = CoroutineScope(Dispatchers.IO + completableJob)

    // Query Observable Field
    val query: MutableLiveData<String> = MutableLiveData()

    // IsLoading Observable Field
    private val _isLoading = MutableLiveData<Boolean>()
    val isLoading: LiveData<Boolean> = _isLoading


    val products: LiveData<List<ProductModel>> = query.switchMap { q ->
        liveData(context = coroutineScope.coroutineContext) {
            emit(emptyList())
            _isLoading.postValue(true)
            val service = MyApplication.getRetrofitService()
            val response = service?.searchProducts(q)
            if (response != null && response.isSuccessful && response.body() != null) {
                _isLoading.postValue(false)
                val body = response.body()
                if (body != null && body.results != null) {
                    emit(body.results)
                }
            } else {
                _isLoading.postValue(false)
            }
        }
    }
}

2 个答案:

答案 0 :(得分:1)

取消父范围时,应该取消改装请求。

{
    "src": "c:/Testcafe/Login.js",
    "browsers": "firefox",
    "reporter": {
        "name": "html",
        "output": "reports/report.html"
    },

    "takeScreenshotsOnFails": true,
    "screenshotPath": "/screenshots/"
}

然后,您需要在需要时取消class ProductSearchViewModel : ViewModel() { val completableJob = Job() private val coroutineScope = CoroutineScope(Dispatchers.IO + completableJob) /** * Adding job that will be used to cancel liveData builder. * Be wary - after cancelling, it'll return a new one like: * * ongoingRequestJob.cancel() // Cancelled * ongoingRequestJob.isActive // Will return true because getter created a new one */ var ongoingRequestJob = Job(coroutineScope.coroutineContext[Job]) get() = if (field.isActive) field else Job(coroutineScope.coroutineContext[Job]) // Query Observable Field val query: MutableLiveData<String> = MutableLiveData() // IsLoading Observable Field private val _isLoading = MutableLiveData<Boolean>() val isLoading: LiveData<Boolean> = _isLoading val products: LiveData<List<ProductModel>> = query.switchMap { q -> liveData(context = ongoingRequestJob) { emit(emptyList()) _isLoading.postValue(true) val service = MyApplication.getRetrofitService() val response = service?.searchProducts(q) if (response != null && response.isSuccessful && response.body() != null) { _isLoading.postValue(false) val body = response.body() if (body != null && body.results != null) { emit(body.results) } } else { _isLoading.postValue(false) } } } } 。下次触发ongoingRequestJob时,由于它将返回一个新作业,因此它应该可以正常运行。您需要做的就是在需要的地方(即在liveData(context = ongoingRequestJob)函数范围内)将其取消。

答案 1 :(得分:1)

您可以通过两种方式解决此问题:

方法1(简易方法)

就像梅尔(Mel)在他的answer中所解释的那样,您可以在switchMap外部保留对作业实例的引用,并取消该作业的实例,然后在switchMap中返回新的liveData。

urllib.request.Request()

方法2(不是很干净,但是可以独立使用并且可重复使用)

由于class ProductSearchViewModel : ViewModel() { // Job instance private var job = Job() val products = Transformations.switchMap(_query) { job.cancel() // Cancel this job instance before returning liveData for new query job = Job() // Create new one and assign to that same variable // Pass that instance to CoroutineScope so that it can be cancelled for next query liveData(CoroutineScope(job + Dispatchers.IO).coroutineContext) { // Your code here } } override fun onCleared() { super.onCleared() job.cancel() } } 构建器块在协程范围内运行,因此您可以结合使用liveData {}和协程CompletableDeffered构建器来暂停该liveData块并手动观察launch liveData启动网络请求作业。

query

您可以在此demo project

中下载并测试这两种方法的运行

编辑:更新了方法1,以在yasir的注释中指出的onCleared方法上添加作业取消。