从片段中的点击事件启动协程

Launch coroutine from click event in fragment

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

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

您需要 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
  }
}

GlobalScope.launch is used if you want to launch a coroutine that is supposed to remain in memory for the entire lifecycle of the app.

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

I assume that if I used GlobalScope.launch, it might keep the fragment from being garbage collected?

仅当协程保留对片段或片段的一部分的引用,且仅当它具有长期运行的潜力。

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

What is the proper way to launch a coroutine from a click event that is defined in a fragment?

最简单的方法是这样的,按照official documentation:

class MyFragment : Fragment, CoroutineScope by MainScope {

    override fun onDestroy() {
        cancel() // extension on CoroutineScope
    }

    ... rest of your fragment code ...
}

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