我的存储库定义如下。
class StoryRepository {
private val firestore = Firebase.firestore
suspend fun fetchStories(): QuerySnapshot? {
return try {
firestore
.collection("stories")
.get()
.await()
} catch(e: Exception) {
Log.e("StoryRepository", "Error in fetching Firestore stories: $e")
null
}
}
}
我也有这样的ViewModel。
class HomeViewModel(
application: Application
) : AndroidViewModel(application) {
private var viewModelJob = Job()
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
private val storyRepository = StoryRepository()
private var _stories = MutableLiveData<List<Story>>()
val stories: LiveData<List<Story>>
get() = _stories
init {
uiScope.launch {
getStories()
}
uiScope.launch {
getMetadata()
}
}
private suspend fun getStories() {
withContext(Dispatchers.IO) {
val snapshots = storyRepository.fetchStories()
// Is this correct?
if (snapshots == null) {
cancel(CancellationException("Task is null; local DB not refreshed"))
return@withContext
}
val networkStories = snapshots.toObjects(NetworkStory::class.java)
val stories = NetworkStoryContainer(networkStories).asDomainModel()
_stories.postValue(stories)
}
}
suspend fun getMetadata() {
// Does some other fetching
}
override fun onCleared() {
super.onCleared()
viewModelJob.cancel()
}
}
如您所见,有时StoryRepository().fetchStories()
可能会失败并返回null
。如果返回值为null
,则在检查snapshots
是否为null
块之后,我不想继续进行后续操作。因此,我想取消该特定的协程(运行getStories()
的协同程序,而不取消另一个协程(运行getMetadata()
的协程)。如何实现这一目标,return
-是withContext
的不当行为?
答案 0 :(得分:2)
尽管您的方法是正确的,但您始终可以进行一些改进以使其更简单或更惯用(特别是当您对自己的代码不满意时)。
这些只是您可能需要考虑的一些建议:
您可以使用Kotlin范围函数,或更具体地说,使用let
函数,如下所示:
private suspend fun getStories() = withContext(Dispatchers.IO) {
storyRepository.fetchStories()?.let { snapshots ->
val networkStories = snapshots.toObjects(NetworkStory::class.java)
NetworkStoryContainer(networkStories).asDomainModel()
} ?: throw CancellationException("Task is null; local DB not refreshed")
}
这样,您将返回数据或如果CancellationException
抛出null
。
在ViewModel中使用协程时,如果将此依赖项添加到gradle文件中,则可以使用CoroutineScope:
androidx.lifecycle:lifecycle-viewmodel-ktx:{version}
因此您可以使用viewModelScope
来构建协程,该协程将在主线程上运行:
init {
viewModelScope.launch {
_stories.value = getStories()
}
viewModelScope.launch {
getMetadata()
}
}
由于Job
具有生命周期意识,因此您可以忘记在onCleared
期间取消其viewModelScope
。
现在剩下要做的就是使用try-catch
块或invokeOnCompletion
构建器返回的Job
上应用的launch
函数来处理异常。 / p>