如何取消 withContext 中的协程 运行?
How do I cancel a coroutine run inside a withContext?
我有一个存储库定义如下。
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()
可能会失败,return null
。如果 return 值为 null
,我不想在检查 snapshots
为 null
块后继续进行后续操作。因此,我想取消那个特定的协程(运行 getStories()
的协程而不取消另一个协程(运行 getMetadata()
的协程)。我如何实现这一点并且是 return
-从 withContext
中学习是一种不良做法?
虽然您的方法是正确的,但您始终可以进行一些改进以使其更简单或更符合习惯(尤其是当您对自己的代码不满意时)。
这些只是您可能需要考虑的一些建议:
您可以使用 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")
}
如果 null
.
这样,您将返回数据或抛出 CancellationException
当您在 ViewModel 中使用协同程序时,如果您将此依赖项添加到 gradle 文件中,您就可以使用 CoroutineScope:
androidx.lifecycle:lifecycle-viewmodel-ktx:{version}
因此您可以使用 viewModelScope
构建您的协程,这将 运行 在主线程上:
init {
viewModelScope.launch {
_stories.value = getStories()
}
viewModelScope.launch {
getMetadata()
}
}
您可以忘记在 onCleared
期间取消它的 Job
,因为 viewModelScope
是生命周期感知的。
现在您要做的就是使用 try-catch
块或 launch
构建器返回的 Job
上应用的 invokeOnCompletion
函数来处理异常.
我有一个存储库定义如下。
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()
可能会失败,return null
。如果 return 值为 null
,我不想在检查 snapshots
为 null
块后继续进行后续操作。因此,我想取消那个特定的协程(运行 getStories()
的协程而不取消另一个协程(运行 getMetadata()
的协程)。我如何实现这一点并且是 return
-从 withContext
中学习是一种不良做法?
虽然您的方法是正确的,但您始终可以进行一些改进以使其更简单或更符合习惯(尤其是当您对自己的代码不满意时)。
这些只是您可能需要考虑的一些建议:
您可以使用 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")
}
如果 null
.
CancellationException
当您在 ViewModel 中使用协同程序时,如果您将此依赖项添加到 gradle 文件中,您就可以使用 CoroutineScope:
androidx.lifecycle:lifecycle-viewmodel-ktx:{version}
因此您可以使用 viewModelScope
构建您的协程,这将 运行 在主线程上:
init {
viewModelScope.launch {
_stories.value = getStories()
}
viewModelScope.launch {
getMetadata()
}
}
您可以忘记在 onCleared
期间取消它的 Job
,因为 viewModelScope
是生命周期感知的。
现在您要做的就是使用 try-catch
块或 launch
构建器返回的 Job
上应用的 invokeOnCompletion
函数来处理异常.