gpt4 book ai didi

android - 如何在 Android 上使用 Coroutines 取消和不中断,包括根据生命周期自动取消?

转载 作者:行者123 更新时间:2023-12-04 23:46:36 28 4
gpt4 key购买 nike

背景
我在从简单(已弃用)迁移时遇到问题 AsyncTaskExecutorsKotlin Coroutines在安卓上
问题
我找不到我可以在 AsyncTask 上完成的基本操作。甚至在 Executors使用 Kotlin Coroutines .
过去,我可以选择在有和没有线程中断的情况下取消任务。现在由于某种原因,给定我在 Coroutines 上创建的任务,它只是没有中断,这意味着如果我运行一些甚至在其中“休眠”的代码(并不总是由我),它不会被中断。
我还记得有人告诉我,协程在 Android 上非常好用,因为如果你在 Activity 中,它会自动取消所有任务。我找不到如何做到这一点的解释。
我尝试并发现的
对于 Coroutines 任务(根据我所见,称为 Deferred),我想我已经读过,当我创建它时,我必须选择它将支持哪个取消,并且由于某种原因我不能同时拥有它们。不确定这是否属实,但我仍然想知道,因为我想同时拥有两者以实现最佳迁移。使用 AsyncTask,我曾经将它们添加到一个集合中(并在取消时删除),以便在 Activity 完成后,我可以检查所有内容并将它们全部取消。我什至做了一个很好的课来为我做这件事。
这是我为测试而创建的:

class MainActivity : AppCompatActivity() {
val uiScope = CoroutineScope(Dispatchers.Main)
val bgDispatcher: CoroutineDispatcher = Dispatchers.IO

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
loadData()
}

private fun loadData(): Job = uiScope.launch {
Log.d("AppLog", "loadData")
val task = async(bgDispatcher) {
Log.d("AppLog", "bg start")
try {
Thread.sleep(5000L) //this could be any executing of code, including things not editable
} catch (e: Exception) {
Log.d("AppLog", "$e")
}
Log.d("AppLog", "bg done this.isActive?${this.isActive}")
return@async 123
}
//simulation of cancellation for any reason, sadly without the ability to cancel with interruption
Handler(mainLooper).postDelayed({
task.cancel()
}, 2000L)
val result: Int = task.await()
Log.d("AppLog", "got result:$result") // this is called even if you change orientation, which I might not want when done in Activity
}
}
构建 gradle 文件:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1"
问题
  • 在 Coroutines 上是否有可能有一个我可以在有和没有线程中断的情况下取消的任务?
  • 应该添加什么来使这项工作,以便当 Activity 死亡(例如方向改变)时,它会被自动取消(选择有或没有中断)?我想我可以使用与 AsyncTask 类似的解决方案,但我记得有人告诉我有一个很好的方法可以为 Coroutines 做这件事。
  • 最佳答案

    协程不是魔术。它们是使用状态机实现的,并且可能有许多暂停点。这一切在原文Coroutines KEEP 中都有解释。 .
    取消发生在这些暂停点上。协程不能在除挂起点之外的任何其他点被取消(至少在正常执行中)。如果您使用 Thread.sleep ,则没有暂停点。您应该使用 delay而不是 sleep ,它引入了一个悬挂点。如果是长操作,可以加几个yield()添加暂停点并使您的协程可取消。
    来自 the docs :

    Coroutine cancellation is cooperative. A coroutine code has to cooperate to be cancellable. All the suspending functions in kotlinx.coroutines are cancellable. They check for cancellation of coroutine and throw CancellationException when cancelled. However, if a coroutine is working in a computation and does not check for cancellation, then it cannot be cancelled


    调用 suspend功能自动引入暂停点。
    正如@CommonsWare 指出的那样,很少有理由创建您的协程范围。在 Activity 和 fragment 中,或与生命周期相关的任何组件中,您应该使用 lifecycleScope .在 ViewModels 中,有 viewModelScope .
    编辑:
    我已经尝试调整 runInterruptible 的来源在特定条件下不中断:传递自定义异常类的实例 InterruptionException作为取消原因将跳过线程中断。我已将 atomicfu 结构替换为 AtomicInteger ,我假设您的目标只是 JVM。您需要通过添加 -Xopt-in=kotlinx.coroutines.InternalCoroutinesApi 来选择加入内部协程 API。编译器标志。
    suspend fun <T> runInterruptibleCancellable(
    context: CoroutineContext = EmptyCoroutineContext,
    block: () -> T
    ): T = withContext(context) {
    try {
    val threadState = ThreadState(coroutineContext.job)
    threadState.setup()
    try {
    block()
    } finally {
    threadState.clearInterrupt()
    }
    } catch (e: InterruptedException) {
    throw CancellationException("Blocking call was interrupted due to parent cancellation").initCause(e)
    }
    }

    private const val WORKING = 0
    private const val FINISHED = 1
    private const val INTERRUPTING = 2
    private const val INTERRUPTED = 3

    private class ThreadState(private val job: Job) : CompletionHandler {
    /*
    === States ===

    WORKING: running normally
    FINISH: complete normally
    INTERRUPTING: canceled, going to interrupt this thread
    INTERRUPTED: this thread is interrupted

    === Possible Transitions ===

    +----------------+ register job +-------------------------+
    | WORKING | cancellation listener | WORKING |
    | (thread, null) | -------------------------> | (thread, cancel handle) |
    +----------------+ +-------------------------+
    | | |
    | cancel cancel | | complete
    | | |
    V | |
    +---------------+ | |
    | INTERRUPTING | <--------------------------------------+ |
    +---------------+ |
    | |
    | interrupt |
    | |
    V V
    +---------------+ +-------------------------+
    | INTERRUPTED | | FINISHED |
    +---------------+ +-------------------------+
    */
    private val _state = AtomicInteger(WORKING)
    private val targetThread = Thread.currentThread()

    // Registered cancellation handler
    private var cancelHandle: DisposableHandle? = null

    fun setup() {
    cancelHandle = job.invokeOnCompletion(onCancelling = true, invokeImmediately = true, handler = this)
    // Either we successfully stored it or it was immediately cancelled
    while (true) {
    when (val state = _state.get()) {
    // Happy-path, move forward
    WORKING -> if (_state.compareAndSet(state, WORKING)) return
    // Immediately cancelled, just continue
    INTERRUPTING, INTERRUPTED -> return
    else -> invalidState(state)
    }
    }
    }

    fun clearInterrupt() {
    /*
    * Do not allow to untriggered interrupt to leak
    */
    while (true) {
    when (val state = _state.get()) {
    WORKING -> if (_state.compareAndSet(state, FINISHED)) {
    cancelHandle?.dispose()
    return
    }
    INTERRUPTING -> {
    /*
    * Spin, cancellation mechanism is interrupting our thread right now
    * and we have to wait it and then clear interrupt status
    */
    }
    INTERRUPTED -> {
    // Clear it and bail out
    Thread.interrupted()
    return
    }
    else -> invalidState(state)
    }
    }
    }

    // Cancellation handler
    override fun invoke(cause: Throwable?) {
    if (cause is InterruptionException) {
    while (true) {
    when (val state = _state.get()) {
    // Working -> try to transite state and interrupt the thread
    WORKING -> {
    if (_state.compareAndSet(state, INTERRUPTING)) {
    targetThread.interrupt()
    _state.set(INTERRUPTED)
    return
    }
    }
    // Finished -- runInterruptible is already complete, INTERRUPTING - ignore
    FINISHED, INTERRUPTING, INTERRUPTED -> return
    else -> invalidState(state)
    }
    }
    }
    }

    private fun invalidState(state: Int): Nothing = error("Illegal state $state")
    }

    class InterruptionException(cause: Throwable?) : CancellationException() {
    init {
    initCause(cause)
    }
    }

    fun Job.interrupt(cause: Throwable? = null) {
    this.cancel(InterruptionException(cause))
    }

    suspend fun Job.interruptAndJoin() {
    interrupt()
    return join()
    }
    您可以使用 interruptinterruptAndJoin触发线程中断的扩展函数,否则使用 cancel用于不间断取消。一个例子:
    val scope = CoroutineScope(Dispatchers.IO)
    val job = scope.launch {
    runInterruptibleCancellable {
    // some blocking code
    Thread.sleep(1000)
    if (!isActive) {
    println("cancelled")
    } else {
    println("completed")
    }
    }
    }
    job.invokeOnCompletion {
    if (it is InterruptionException) {
    print("interrupted")
    }
    }

    runBlocking {
    // job.interruptAndJoin() // prints "interrupted"
    // job.cancelAndJoin() // prints "cancelled"
    job.join() // prints "completed"
    }
    这个例子是我做过的唯一测试。它似乎工作。我不知道它是否泄漏,我不知道它是否是线程安全的。老实说,我真的离我的专业知识还很远。如果没有进一步确认它可以工作,请不要在生产中使用它。

    关于android - 如何在 Android 上使用 Coroutines 取消和不中断,包括根据生命周期自动取消?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65973215/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com