【问题标题】:Launch not executing when called inside a coroutinescope在协程范围内调用时启动未执行
【发布时间】:2021-02-01 05:48:44
【问题描述】:

我在协程范围内调用启动,但它没有执行。 Snackbar 动作监听器正在执行,但启动块由于某种原因没有执行。

CoroutineScope(Dispatchers.Main).launch {
                val scope = this
                val mn = snackbarManager(R.id.root);
                Snackbar.make(mn.container, R.string.recpt_deleted, Snackbar.LENGTH_LONG)
                    .setAction(R.string.undo) {
                        scope.launch {**//not executing**
                            Toast.makeText(requireContext(),"Committing",Toast.LENGTH_LONG).show()
                            Log.d("COMMIT", "calling commit")
                           
                        }
                    }
                .show()
            }

【问题讨论】:

    标签: android kotlin kotlin-coroutines android-snackbar coroutinescope


    【解决方案1】:

    您在 Snackbar 操作侦听器中使用的 scope 与您用于调用第一个 launch 函数的范围不同。要解决这个问题,您可以参考主要的CoroutineScope

    val scope = CoroutineScope(Dispatchers.Main)
    scope.launch {
        // ...
        // in action listener:
        scope.launch { /*...*/ }
    }
    

    或者在Snackbar动作监听器中使用另一个CoroutineScope,例如lifecycleScope属性:

    lifecycleScope.launch {
        Toast.makeText(requireContext(),"Committing",Toast.LENGTH_LONG).show()
        Log.d("COMMIT", "calling commit")
    }
    

    但在我看来,您的代码有点混乱。我认为您应该重新考虑您的方法,不要使用CoroutineScope 来显示Snackbar

    更新:

    当您在第一个协程val scope = this 中初始化scope 变量时,当外部协程完成时,这个scope 变为COMPLETED。当您在 Snackbar 动作监听器中启动内部协程时,scope 已经具有 COMPLETED 状态。我们无法使用具有 COMPLETED 状态的 CoroutineScope 启动协程。

    【讨论】:

    • 我的理解是无论使用哪个范围,启动都应该始终执行。我的理解错了吗?
    【解决方案2】:

    对于第二个 scope launch 你应该使用 Dispatchers.Main 因为:

    Main coroutine dispatcher 仅限于使用 UI 对象操作的主线程。通常这样的dispatchers 是单线程的。

    第一种方式:

    那么你应该把你的代码改成这样:

    CoroutineScope(Dispatchers.Main).launch {
        Snackbar.make(requireView(),"milad", Snackbar.LENGTH_LONG)
            .setAction("clickMe"){
                CoroutineScope(Dispatchers.Main).launch {
                    Toast.makeText(requireContext(),"Committing", Toast.LENGTH_LONG).show()
                    Log.d("COMMIT", "calling commit")
                }
            }.show()
    }
    

    更新:

    第二种方式:

       val coro= CoroutineScope(Dispatchers.Main)
    
        coro.launch {
            Snackbar.make(requireView(),"milad", Snackbar.LENGTH_LONG)
                .setAction("clickMe"){
                    coro.launch {
                        Toast.makeText(requireContext(),"Committing", Toast.LENGTH_LONG).show()
                        Log.d("COMMIT", "calling commit")
                    }
                }.show()
        }
    

    【讨论】:

    • 第一个CoroutineScope也使用Dispatchers.Main,所以这不是问题。
    • 不,它不使用Dispatchers.Main,如果它使用它,它工作得很好,通过更新我的答案,它会在第二个范围内再次使用Dispatchers.Main
    • @miladsalimi 不应该执行无论使用哪个范围的启动块?
    猜你喜欢
    • 2014-02-28
    • 1970-01-01
    • 2019-05-02
    • 2020-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-22
    • 2017-12-11
    相关资源
    最近更新 更多