【问题标题】:Launch coroutine from click event in fragment从片段中的点击事件启动协程
【发布时间】:2020-01-06 08:36:24
【问题描述】:

从片段中定义的点击事件启动协程的正确方法是什么?据我了解,如果您想启动一个应该在应用程序的整个生命周期内保留在内存中的协程,则使用GlobalScope.launch。但由于片段的生命周期通常比应用程序短,GlobalScope.launch 可能不是正确的方法。我假设如果我使用GlobalScope.launch,它可能会阻止碎片被垃圾收集?

我真的只需要从 click 事件处理程序中启动协程,这意味着我不会从中调用任何父函数。

【问题讨论】:

    标签: kotlin kotlin-coroutines


    【解决方案1】:

    您需要一个job 来处理协程取消以防止泄漏:

    //inside Fragment
    val job = Job()
    val uiScope = CoroutineScope(Dispatchers.Main + job)
    
    
    //late in the button click
    
    button.setOnClickListener{
      uiScope.launch(Dispatchers.IO){
        //asyncOperation
        withContext(Dispatchers.Main){
         //ui operation
       }
    
      }
    }
    
    //later in on destroy:
    
    override fun onDestroy(){
      job.cancel()
      super.onDestroy()
    }
    

    您也可以使用来自 Google 的LifecycleScope extension

    class MyFragment: Fragment() {
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            lifecycleScope.launch {
                val params = TextViewCompat.getTextMetricsParams(textView)
                val precomputedText = withContext(Dispatchers.Default) {
                    PrecomputedTextCompat.create(longTextContent, params)
                }
                TextViewCompat.setPrecomputedText(textView, precomputedText)
            }
        }
    }
    

    编辑,以防您重新启动另一个操作。只需使用相同的范围:

    //let's assume you have a second button
    
    button2.setOnClickListener{
      uiScope.launch(Dispatchers.IO){
        //perform second operation
      }
    }
    

    【讨论】:

    • 你是说如果你在fragment被销毁的时候不调用job.cancel,协程就会让fragment的那个实例保持存活?
    • 不是片段的实例,但线程将保持未关闭状态
    • 但是如果fragment被销毁了,线程绑定到fragment上怎么能保持未关闭状态呢?
    • 但是如果第一个点击事件还没有完成它的协程,而你做了一个不同的点击事件来重用同一个协程,那不是问题吗?我认为您使用 LifecycleScope 的解决方案可能是可行的方法。
    • 这取决于您的用例。在异步操作完成之前,您可以禁用该按钮。
    【解决方案2】:

    GlobalScope.launch 用于启动一个应该在应用程序的整个生命周期内保留在内存中的协程。

    不一定如此,它可以用于任何与用户可以导航离开的应用程序的活动或阶段不耦合的协程。例如,您可能会启动一项向您的服务器发送单向消息的任务。它可能很快就会完成,您希望它完成用户稍后所做的任何事情。

    我假设如果我使用GlobalScope.launch,它可能会阻止片段被垃圾收集?

    仅当协程保留对片段或其一部分的引用,并且仅当它有可能长时间运行时。

    具体来说,您在点击事件中执行的典型操作是执行一些涉及您的后端(即网络)的操作,然后更新 UI。显然,这可能需要很长时间(尤其是在网络不好的情况下),并且它保留了对稍后将要触及的 UI 元素的引用。这应该绑定到片段的生命周期。

    从片段中定义的点击事件启动协程的正确方法是什么?

    最简单的方法是这样,跟随official documentation:

    class MyFragment : Fragment, CoroutineScope by MainScope {
    
        override fun onDestroy() {
            cancel() // extension on CoroutineScope
        }
    
        ... rest of your fragment code ...
    }
    

    这捕获了您以前必须手写的习语(如此处的另一个答案所示)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-26
      相关资源
      最近更新 更多