【问题标题】:Kotlin CoroutineScope can't cancel in android viewsKotlin CoroutineScope 无法在 android 视图中取消
【发布时间】:2020-01-21 04:18:30
【问题描述】:

例如,这个视图。当onDetachedFromWindow调用范围被取消,但启动的作业仍然处于活动状态。

class TestView : FrameLayout,CoroutineScope {
    val TAG = "TestView"
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + Job()

    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        launch {
            while (true) {
                Log.i(TAG,"is in launch coroutine....${coroutineContext} ${this@TestView.coroutineContext}")
                delay(1000)
            }
        }
    }
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    )

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        cancel()
        Log.i(TAG,"onDetachedFromWindow")
    }
}

日志是

2019-09-19 21:32:26.652 22912-22912/com.ymr.myapplication I/TestView: is in launch coroutine....[StandaloneCoroutine{Active}@7f9d20f, Main] [JobImpl{Active}@2f3fde2, Main]
2019-09-19 21:32:27.655 22912-22912/com.ymr.myapplication I/TestView: is in launch coroutine....[StandaloneCoroutine{Active}@7f9d20f, Main] [JobImpl{Active}@80a2573, Main]
2019-09-19 21:32:28.656 22912-22912/com.ymr.myapplication I/TestView: is in launch coroutine....[StandaloneCoroutine{Active}@7f9d20f, Main] [JobImpl{Active}@dad2c30, Main]
2019-09-19 21:32:29.649 22912-22912/com.ymr.myapplication I/TestView: onDetachedFromWindow
2019-09-19 21:32:29.665 22912-22912/com.ymr.myapplication I/TestView: is in launch coroutine....[StandaloneCoroutine{Active}@7f9d20f, Main] [JobImpl{Active}@dab39f4, Main]
2019-09-19 21:32:30.666 22912-22912/com.ymr.myapplication I/TestView: is in launch coroutine....[StandaloneCoroutine{Active}@7f9d20f, Main] [JobImpl{Active}@448351d, Main]
2019-09-19 21:32:31.668 22912-22912/com.ymr.myapplication I/TestView: is in launch coroutine....[StandaloneCoroutine{Active}@7f9d20f, Main] [JobImpl{Active}@45ba392, Main]
2019-09-19 21:32:32.669 22912-22912/com.ymr.myapplication I/TestView: is in launch coroutine....[StandaloneCoroutine{Active}@7f9d20f, Main] [JobImpl{Active}@bc75163, Main]

那么为什么不能取消范围启动的作业呢?

【问题讨论】:

    标签: android kotlin coroutine kotlin-coroutines


    【解决方案1】:

    你可以这样试试,

    class TestView : FrameLayout, CoroutineScope {
    val TAG = "TestView"
    private lateinit var job: Job
    
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + job
    
    constructor(context: Context) : super(context)
    
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        job = Job()
    
        launch {
            while (true) {
                Log.i(TAG,"is in launch coroutine....${coroutineContext} ${this@TestView.coroutineContext}")
                delay(1000)
            }
        }
    }
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    )
    
    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        job.cancel()
        Log.i(TAG,"onDetachedFromWindow")
    }
    }
    

    【讨论】:

    • 考虑将 Job 构造函数调用移到 onAttachedToWindow() 方法调用中,因为 View 构造一次,但可以多次附加到一个 Window。
    【解决方案2】:

    问题是您每次都在coroutineContext 中创建一个新的Job。您可以使用

    轻松解决此问题
    override val coroutineContext = Dispatchers.Main + Job()
    

    但是,请记住,如果您的视图在分离后附加到窗口,coroutineContext 将已被取消。

    查看以下示例以更好地了解其工作原理:

    class MyActivity : AppCompatActivity(), CoroutineScope {
        lateinit var job: Job
        override val coroutineContext: CoroutineContext
            get() = Dispatchers.Main + job
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            job = Job()
        }
    
        override fun onDestroy() {
            super.onDestroy()
            job.cancel() // Cancel job on activity destroy. After destroy all children jobs will be cancelled automatically
        }
    
        /*
         * Note how coroutine builders are scoped: if activity is destroyed or any of the launched coroutines
         * in this method throws an exception, then all nested coroutines are cancelled.
         */
        fun loadDataFromUI() = launch { // <- extension on current activity, launched in the main thread
           val ioData = async(Dispatchers.IO) { // <- extension on launch scope, launched in IO dispatcher
               // blocking I/O operation
           }
           // do something else concurrently with I/O
           val data = ioData.await() // wait for result of I/O
           draw(data) // can draw in the main thread
        }
    }
    

    【讨论】:

    • 如何在 View 中使用相同的工作 我不明白您对上述问题的解释!
    • 为什么你需要这份工作迟到?你不能打电话。 this.cancel 作用域本身?
    猜你喜欢
    • 1970-01-01
    • 2020-04-09
    • 1970-01-01
    • 2021-10-18
    • 2021-06-13
    • 1970-01-01
    • 2020-09-10
    • 2023-01-12
    • 2020-08-23
    相关资源
    最近更新 更多