Android 如何取消withContext中的协同程序运行?

Android 如何取消withContext中的协同程序运行?,android,kotlin,coroutine,kotlin-coroutines,withcontext,Android,Kotlin,Coroutine,Kotlin Coroutines,Withcontext,我有一个如下定义的存储库 class StoryRepository { private val firestore = Firebase.firestore suspend fun fetchStories(): QuerySnapshot? { return try { firestore .collection("stories") .get()

我有一个如下定义的存储库

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
        }
    }
}
我还有一个像这样的视图模型

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()
    }
}
类HomeViewModel(
应用程序:应用程序
):AndroidViewModel(应用程序){
私有变量viewModelJob=Job()
private val uiScope=CoroutineScope(Dispatchers.Main+viewModelJob)
private val storyRepository=storyRepository()
私有var_stories=MutableLiveData()
val故事:LiveData
获取你的故事
初始化{
发射{
getStories()
}
发射{
getMetadata()
}            
}
私人故事{
withContext(Dispatchers.IO){
val snapshots=storyRepository.fetchStories()
//这是正确的吗?
如果(快照==null){
cancel(CancellationException(“任务为空;本地数据库未刷新”))
return@withContext
}
val networkStories=snapshots.toObjects(NetworkStory::class.java)
val stories=NetworkStoryContainer(networkStories).asDomainModel()
_故事。postValue(故事)
}
}
挂起元数据(){
//还有其他的吸引人的事吗
}
覆盖有趣的onCleared(){
super.onCleared()
viewModelJob.cancel()
}
}

如您所见,有时,
StoryRepository().fetchStories()
可能会失败并返回
null
。如果返回值为
null
,则我不希望在检查
snapshots
null
块后继续执行后续操作。因此,我想取消这个特定的协同程序(运行
getStories()
的一个,而不取消另一个协同程序(运行
getMetadata()
)。我如何实现这一点?使用上下文从
返回
-ing是否是一种不好的做法?

尽管您的方法是正确的,但您始终可以进行一些改进,使其更简单或更惯用(尤其是当您对自己的代码不满意时)

以下是您可能需要考虑的一些建议:

您可以使用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文件中,则可以使用协同路由

androidx.lifecycle:lifecycle-viewmodel-ktx:{version}
因此,您可以使用
viewModelScope
构建将在主线程上运行的协同程序:

init {
    viewModelScope.launch {
        _stories.value = getStories()
    }

    viewModelScope.launch {
        getMetadata()
    }
}
您可以忘记在
onCleared
期间取消其
作业
,因为
viewModelScope
具有生命周期意识


现在,您所要做的就是使用
try catch
块或
invokeOnCompletion
函数处理异常,该函数应用于
launch
生成器返回的
作业

在@AnimeshSahu看来是正确的
使用上下文从
返回
可以吗捕获
取消异常的机制
?如果我想捕获
取消异常
,我如何使用您的代码实现这一点?我是否要用上下文(Dispatchers.IO)包装整个
try catch
块阻塞?@Richard如果在
启动
生成器中用
try catch
块包装它的调用,会更容易。或者你可以使用
作业
所具有的
invokeOnCompletion
函数。它的可丢弃性不会是
null
,这就是你知道发生了什么的原因我明白了,它会是这样的:
viewModelScope.launch{u stories.value=getStories}.invokeonpletion{cause->catch(cause:CancellationException){…}
?@Richard更像:
.invokeonpletion{cause->cause?.apply{…}
。问题是,如果出现问题,
原因
将不会为
null
。您可以在
apply
块内执行任何您想执行的操作,或者执行任何您使用的操作。我想您的意思是,如果没有任何问题,原因将是
null
?您能否捕获
apply
块中的异常?