如何取消正在运行的LiveData协程块 [英] How to cancel a running LiveData Coroutine Block
问题描述
通过使用LiveData的最新版本"androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-alpha03",我使用LiveData的新构件(LiveData +协程(Coroutine),可使用Retrofit执行同步网络调用,并相应地更新ViewModel中的不同标志(isLoading,isError).我在查询" LiveData上使用Transforamtions.switchMap,因此无论何时从UI中对查询"进行更改,搜索产品"代码都将使用Transformations.switchMap开始执行.一切正常,除了查询" LiveData中发生更改时我想取消以前的改装呼叫".目前,我看不到任何方法可以做到这一点.任何帮助,将不胜感激.
By using LiveData's latest version "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-alpha03", I have developed a code for a feature called "Search Products" in the ViewModel using LiveData's new building block (LiveData + Coroutine) that performs a synchronous network call using Retrofit and update different flags (isLoading, isError) in ViewModel accordingly. I am using Transforamtions.switchMap on "query" LiveData so whenever there is a change in "query" from the UI, the "Search Products" code starts its executing using Transformations.switchMap. Every thing is working fine, except that i want to cancel the previous Retrofit Call whenever a change happens in "query" LiveData. Currently i can't see any way to do this. Any help would be appreciated.
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)
}
}
}
}
推荐答案
您可以通过两种方式解决此问题:
You can solve this problem in two ways:
方法1(简易方法)
就像梅尔(Mel)在其答案中所解释的那样,您可以将引用保持在switchMap外部的作业实例并取消立即在switchMap中返回新的liveData之前立即执行该作业.
Just like Mel has explained in his answer, you can keep a referece to the job instance outside of switchMap and cancel instantance of that job right before returning your new liveData in switchMap.
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()
}
}
方法2(不是很干净,但是可以独立使用并且可重复使用)
由于 liveData {}
构建器块在协程范围内运行,因此您可以结合使用 CompletableDeffered
和协程 launch
构建器来暂停liveData阻止并手动观察 query
liveData以启动网络请求作业.
Since liveData {}
builder block runs inside a coroutine scope, you can use a combination of CompletableDeffered
and coroutine launch
builder to suspend that liveData block and observe query
liveData manually to launch jobs for network requests.
class ProductSearchViewModel : ViewModel() {
private val _query = MutableLiveData<String>()
val products: LiveData<List<String>> = liveData {
var job: Job? = null // Job instance to keep reference of last job
// LiveData observer for query
val queryObserver = Observer<String> {
job?.cancel() // Cancel job before launching new coroutine
job = GlobalScope.launch {
// Your code here
}
}
// Observe query liveData here manually
_query.observeForever(queryObserver)
try {
// Create CompletableDeffered instance and call await.
// Calling await will suspend this current block
// from executing anything further from here
CompletableDeferred<Unit>().await()
} finally {
// Since we have called await on CompletableDeffered above,
// this will cause an Exception on this liveData when onDestory
// event is called on a lifeCycle . By wrapping it in
// try/finally we can use this to know when that will happen and
// cleanup to avoid any leaks.
job?.cancel()
_query.removeObserver(queryObserver)
}
}
}
您可以在演示项目
更新了方法1,以在yasir的注释中指出的onCleared方法上添加作业取消.
Updated Method # 1 to add job cancellation on onCleared method as pointed out by yasir in comments.
这篇关于如何取消正在运行的LiveData协程块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!