通过使用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)
}
}
}
}
答案 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方法上添加作业取消。