关于 Kotlin 协程取消的问题

ada*_*sha 3 android coroutine kotlin

我的应用程序中有一个类似于以下的代码

class MyFragment : Fragment(), CoroutineScope by MainScope() {

    override fun onDestroy() {
        cancel()
        super.onDestroy()
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        doSomething()
    }

    private fun doSomething() = launch {
        val data = withContext(Dispathers.IO) {
            getData()
        }

        val pref = context!!.getSharedPreferences("mypref", MODE_PRIVATE)
        pref.edit().putBoolean("done", true).apply()
    }
}
Run Code Online (Sandbox Code Playgroud)

在生产中,我得到很多NPEsdoSomething(),而访问context

我的假设是coroutine调用cancel()in后被取消onDestroy(),所以我没有费心检查context空值。但看起来continues即使在cancel()被调用之后也能执行。我认为这会发生,如果cancel()在完成后调用withContext和恢复之前coroutines

所以我替换doSomething()了以下内容。

    private fun doSomething() = launch {
        val data = withContext(Dispathers.IO) {
            getData()
        }

        if (isActive) {
            val pref = context!!.getSharedPreferences("mypref", MODE_PRIVATE)
            pref.edit().putBoolean("done", true).apply()
        }
    }
Run Code Online (Sandbox Code Playgroud)

这修复了崩溃。

但是,这是预期的行为还是我做错了什么?Kotlin 的文档对此不是很清楚。而且网上的大部分例子都像我原来的代码。

Val*_*kov 5

withContext()如果范围被取消,您的代码假定当它返回时将停止执行,但实际上它没有,直到kotlin 协程的1.3.0版。这是 GitHub问题。我猜您正在使用该库的早期版本。

我还建议您使用LifecycleScope而不是自定义范围。它是lifecycle-runtime-ktx图书馆的一部分。因此,简化的解决方案如下所示:

// build.gradle
dependencies {
    ...
    implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-rc02"
}
Run Code Online (Sandbox Code Playgroud)
class MyFragment : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        doSomething()
    }

    private fun doSomething() = viewLifecycleOwner.lifecycleScope.launch {
        val data = withContext(Dispathers.IO) {
            getData()
        }

        val pref = context!!.getSharedPreferences("mypref", MODE_PRIVATE)
        pref.edit().putBoolean("done", true).apply()
    }
}
Run Code Online (Sandbox Code Playgroud)

还有其他有用的实用程序可以简化协程的使用,请查看使用 Kotlin 协程与架构组件文档部分。