我正在当前Android应用程序的多个Fragment之间共享一个ActivityScoped viewModel。
viewModel使用协程范围viewModelScope.launch{}
我的问题是.launch{}
仅在调用拥有的ViewModel
onCleared()
方法之前有效。
这是ViewModel范围的协程应该工作的方式吗?
是否可以使用一种方法来“重置” viewModelScope,以便.launch {}在调用onCleared()方法之后起作用?
这是我的代码:
片段
RxSearchView.queryTextChangeEvents(search)
.doOnSubscribe {
compositeDisposable.add(it)
}
.throttleLast(300, TimeUnit.MILLISECONDS)
.debounce(300, TimeUnit.MILLISECONDS)
.map { event -> event.queryText().toString() }
.observeOn(AndroidSchedulers.mainThread())
.subscribe { charactersResponse ->
launch {
viewModel.search(charactersResponse.trim())
}
}
。 。
override fun onDetach() {
super.onDetach()
viewModel.cancelSearch()
compositeDisposable.clear()
}
ViewModel
suspend fun search(searchString: String) {
cancelSearch()
if (TextUtils.isEmpty(searchString)) {
return
}
job = viewModelScope.launch {
repository.search(searchString)
}
}
fun cancelSearch() {
job?.cancelChildren()
}
。 。
override fun onCleared() {
super.onCleared()
repository.onCleared()
}
我在做什么错了?
更新
如果我将启动代码修改为此
job = GlobalScope.launch {
repository.search(searchString)
}
它解决了我的问题,但这是达到我期望的结果的唯一方法吗?
我对GlobalScope
的印象很“不好”
答案 0 :(得分:2)
将cal设置为onCleared()之后,我的viewModelScoped cororoutine启动将停止执行
这是一个功能,而不是错误。
一旦清除ViewModel
,您就不应在该ViewModel
中进行任何操作,无论其LifecycleOwner
是什么。所有这些现在都已失效,不再应该使用。
但这是达到我期望的结果的唯一方法吗?
正确的解决方案是摆脱ViewModel
中的代码。如果您期望某些背景工作超出活动或片段的生命周期,则该代码不属于活动/片段或其关联的视图模型。它属于可以与您要完成的工作相匹配的生命周期的事物。
答案 1 :(得分:1)
repository.onCleared()
此方法不应属于存储库。
实际上,存储库不应是有状态的。
如果您查看Google的示例the Repository creates a LiveData
that contains a Resource
,并且发现与之相关的原因是因为the actual data loading and caching mechanic is inside this resource, triggered by LiveData.onActive
(在此示例中,MediatorLiveData.addSource
,但从语义上讲,这是同一件事)。 / p>
.subscribe { charactersResponse -> launch { viewModel.search(charactersResponse.trim())
该片段不应该启动协同程序。它应该说类似
.subscribe {
viewModel.updateSearchText(charactersResponse.trim())
}
还有
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java, factory)
viewModel.searchResults.observe(viewLifecycleOwner, Observer { results ->
searchAdapter.submitList(results)
})
}
然后ViewModel将
class MyViewModel(
private val repository: MyRepository
): ViewModel() {
private val searchText = MutableLiveData<String>()
fun updateSearchText(searchText: String) {
this.searchText.value = searchText
}
val searchResults: LiveData<List<MyData>> = Transformations.switchMap(searchText) {
repository.search(searchText)
}
}
这就是ViewModel中的全部内容,那么问题是“谁拥有协程范围”?这取决于何时取消任务。
如果“不再观察”应该取消任务,那么取消任务应该是LiveData.onInactive()
。
如果“不再观察但不再清除”应保留该任务,则ViewModel的onCleared实际上应控制将在onCleared()
中取消的ViewModel中的SupervisorJob,并且应在其中启动search
该范围,只有将CoroutineScope传递给search
方法时,才有可能。
suspend fun search(scope: CoroutineScope, searchText: String): LiveData<List<T>> =
scope.launch {
withContext(Dispatchers.IO) { // or network or something
val results = networkApi.fetchResults(searchText)
withContext(Dispatchers.MAIN) {
MutableLiveData<List<MyData>>().apply { // WARNING: this should probably be replaced with switchMap over the searchText
this.value = results
}
}
}
}
这项工作吗?不确定,我实际上并不使用协程,但我认为应该使用。但是,此示例无法处理与LiveData内部的switchMap
相同的操作,也无法使用协程。