【问题标题】:How should I restart/retry Kotlin coroutine after exception in Android在Android中出现异常后我应该如何重新启动/重试Kotlin协程
【发布时间】:2019-04-30 12:33:33
【问题描述】:

我很高兴我将长时间运行的任务切换为 UI 线程协程。与 Android 中的 AsyncTask 或常规线程相比,它提高了性能并将内存使用量减少了 3 倍,并且所有内存泄漏都消失了。

唯一的问题是,在异常在某个时间发生...

之后,我不知道应该如何重新启动长时间运行的操作

阅读大量文章后,我觉得我根本不了解协程中的异常处理。让我知道如何实现所需的行为。

  1. 我在片段中有协程范围(将在不久的将来移动到 VM)。

    lateinit var initEngineJob: Job
    override val coroutineContext: CoroutineContext
        get() = initEngineJob + Dispatchers.Main

  1. 使用 async/await 长时间运行的任务。

fun initWorkEngineCoroutine()
    {
        launch {

            while(true) {
                val deferred = async(Dispatchers.Default) {
                    getResultsFromEngine()
                }
                    val result = deferred.await()
                    if (result != null) {
                    //UI thread
                        draw!!.showResult(result)
                    }
                }
            }

        }

fun getResultsFromEngine() :Result? {
           result = // some results from native c++ engine, which throws exception at some times
           return result
   }

我不知道我应该把try catch放在哪里。我试图用 try catch 包围 deferred.await(),但我可以不能在 catch 块中调用相同的方法来重试长时间运行的任务。我尝试了 SupervisorJob(),但也没有成功。我仍然无法再次调用 initWorkEngineCoroutine()启动新的协程...

帮助终于解决了这个问题:)

【问题讨论】:

  • 你从来没有给initEngineJob分配任何东西,你也没有使用coroutineContext——你的样本不完整吗?
  • 我在 onCreate 中分配 initiEngineJob。如果我在 Fragment 中使用 coroutineContext 是否已经被覆盖? Android Studio 显示它选择了正确的 coroutineContext

标签: android kotlin android-memory kotlin-coroutines


【解决方案1】:

您应该将您的代码视为线性命令式,并在代码中最符合逻辑的地方尝试/捕获。有了这种心态,您的问题可能不是关于协程,而是更多关于 try/catch 重试。你可能会这样做:

fun main() {
    GlobalScope.launch {
        initWorkEngineCoroutine()
    }
}

suspend fun initWorkEngineCoroutine() {
    var failures = 0
    val maxFailures = 3
    while(failures <= maxFailures) {
        try {
             getResultsFromEngine()?.let {
                draw!!.showResult(it)
            }
        } catch (e: Exception) {
            failures++
        }
    }
}

// withContext is like async{}.await() except an exception occuring inside 
// withContext can be caught from inside the coroutine.
// here, we are mapping getResultFromEngine() to a call to withContext and 
// passing withContext the lambda which does the work
suspend fun getResultsFromEngine() :Result? = withContext(Dispatchers.Default) {
    Result()
}

我已经包含了一些逻辑来防止无限循环。它可能不符合您的要求,但您可能会考虑采取某种措施来防止getResultsFromEngine() 立即引发异常并最终导致可能导致意外行为和潜在堆栈溢出的无限循环。

【讨论】:

  • 只有一条评论,withContext 不是async {}.await() 的简写,它们在异常处理方面完全不同。 async 中的异常超出其范围将导致 async 协程被取消并将取消传播到父协程。无论您是否将await 放入try-catch,都会发生这种情况。由于withContext 没有创建新的协程,逃逸的异常不会取消任何东西,它会落在周围的try-catch
  • @MarkoTopolnik 谢谢。我承认自从转向结构化并发以来我忽略了一些额外的研究。我的答案已更新,我认为对此事的评论更准确。
  • 感谢你们两位的明确解释。我似乎终于明白了:)会试试这个,很快就会让你(接受答案)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-21
  • 2018-10-24
相关资源
最近更新 更多