携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的13天,点击查看活动详情
为了活动小家电,接着上篇搞!
延迟取消
即使job处理得好,也可以终止。这部分很容易解决,但是在使用while语句的时候其实有一个问题,在cancel发生的时候很重要。
当此代码检查 isActive 时,它会根据 isActive 的状态终止。
private suspend fun suspendLoop() = withContext(Dispatchers.IO) {
while (isActive) {
println("工作存活着")
delay(1)
}
}
如果你只是简单地操作如下图的无限循环,它是行不通的。
private suspend fun suspendLoop() = withContext(Dispatchers.IO) {
while (true) {
println("工作存活着")
delay(1)
}
}
如果你看这段代码,你可以看到至少需要使用 isActive 来控制无限循环。
在 AAC ViewModel 中使用协程的推荐方式
这一段,我们将看看如何在 Android 中使用最多的 ViewModel 中使用协程。
class TestViewModel: ViewModel() {
init {
viewModelScope.launch {
// 清除 ViewModel 时将取消的协程。
}
}
}
viewModelScop 在这里谈论什么?
这个 viewModelScop 对应于一个可以使用 ViewModel ktx 的扩展。
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:lastVersion'
所以,稍微看一下内部代码,如下所示,和之前看到的 MainScope() 的代码是一样的,只是 Dispatches 的用法略有不同。
/**
* ViewModel 绑定一个CoroutineScope,当viewmodel的onCleared被回调时取消该协程作用域
*/
val ViewModel.viewModelScope: CoroutineScope
get() {
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) {
return scope
}
return setTagIfAbsent(JOB_KEY,
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate))
}
internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
override val coroutineContext: CoroutineContext = context
override fun close() {
coroutineContext.cancel()
}
}
那么这个协程什么时候结束呢?最后的可关闭继承部分很重要。
实现Closeable,在AAC-ViewModel的内部代码中有明确的,在这段代码中有一个叫closeWithRuntimeException的代码。
正如您在这段代码中看到的,如果它是 Closeable,您可以看到可以再次调用 close。
综上所述,Android onDestroy 调用的时候,ViewModel 的 clear 被调用,并且 closeWithRuntimeException 被调用,自然也就结束了 AAC-ViewModel 的 viewModelScope。关于ViewModel的源码解析可以参考我之前的文章
public abstract class ViewModel {
@MainThread
final void clear() {
mCleared = true;
if (mBagOfTags != null) {
synchronized (mBagOfTags) {
for (Object value : mBagOfTags.values()) {
closeWithRuntimeException(value);
}
}
}
onCleared();
}
private static void closeWithRuntimeException(Object obj) {
if (obj instanceof Closeable) {
try {
((Closeable) obj).close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
总结
我们检查了 CoroutineScope 的各种方法。
他们中的一些人自己照顾自己,但最终有很多部分是用户需要了解和使用的。如上所见,如果使用 MainScope 并使用 CoroutineScope,则终止处理是必不可少的。
并且发现取消也不一定无条件终止。
简而言之
- 需要根据生命周期使用范围。
- 根据生命周期,必须调用范围的取消。
- 取消并不意味着所有范围都被无条件终止。
- 在无限循环中,要取消,必须检查 isActive。