Kotlin CoroutineScope can't cancel in android views

試著忘記壹切 提交于 2020-01-13 18:27:52

问题


For example,this view.When the onDetachedFromWindow invoke the scope is cancelled,but the launched job is still active.

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")
    }
}

the log is

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]

So why can't cancel the scope launched jobs?


回答1:


The issue is that you're creating a new Job in coroutineContext every time. You can fix this easily with

override val coroutineContext = Dispatchers.Main + Job()

However, please bear in mind that if your view is attached to window after being detached the coroutineContext will already be cancelled.

Take a look at the following example to get a better picture of how it should work:

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
    }
}



回答2:


You may try like this,

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")
}
}


来源:https://stackoverflow.com/questions/58022726/kotlin-coroutinescope-cant-cancel-in-android-views

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!