【问题标题】:Coroutines best way to use协程的最佳使用方式
【发布时间】:2019-03-09 23:58:03
【问题描述】:

我是协程新手。所以我只是想知道使用它们的最佳方式是什么。

我的场景/用例是我想在 IO thread 上进行 API 调用并在 Main thread 上观察结果并更新 UI。另外,当调用片段的onDestoryView() 时,我想取消我的工作。

我的片段要求演示者提供一些更新。所以我的演示者有一个像这样运行的协程 -

class MyPresenter(view: MyView,
                          private val coroutineCtx: CoroutineContext = Dispatchers.Main) : CoroutineScope {

    private val job: Job = Job()
    private var view: MyView? = null

    init {
        this.view= view
    }

    override val coroutineContext: CoroutineContext
        get() = job + coroutineCtx

    fun updateData() = launch{
        //repo is singleton
        val scanResult = repo.updateData()
        when(scanResult) {
        sucess -> { this.view.showSuccess()}
        }
    }

    fun stopUpdate() { 
        job.cancel()
    }
}

在我的存储库中,

suspend fun updateData(): Result<Void> {
   val response = API.update().await()
   return response
}

我是否正确使用协程?如果是,我的job.cancel() 似乎永远不会工作,尽管我从片段的onDestroyView() 调用它。

【问题讨论】:

  • 我听说最好使用 SupervisorJob 而不是 Job 作为协程范围的根作业。

标签: android kotlin kotlin-coroutines


【解决方案1】:

从我的角度来看,您正确地使用了协程。几点说明:

  • 您不必将view: MyView 传递给构造函数,并将其值分配给init 块中的属性。相反,您可以将构造函数中的view 参数标记为val,它将成为一个属性:

    class MyPresenter(private val view: MyView,
                      private val coroutineCtx: CoroutineContext = Dispatchers.Main) : CoroutineScope {
    
        // you can get rid of the next lines:
        private var view: MyView? = null
        init {
            this.view= view
        }
    
    }
    
  • launch 函数返回一个Job。您可以添加扩展功能,例如launchSilent,返回Unit

    fun CoroutineScope.launchSilent(
        context: CoroutineContext = EmptyCoroutineContext,
        start: CoroutineStart = CoroutineStart.DEFAULT,
        block: suspend CoroutineScope.() -> Unit
    ) {
         launch(context, start, block)
    }
    
  • 根据我的观察,job.cancel() 工作正常:当您调用它时,协程必须停止。例如,如果我们放一些日志:

    fun updateData() = launch{
        Log.d("Tag", "launch start")
        val scanResult = repo.updateData()
        when(scanResult) {
            success -> { this.view.showSuccess()}
        }
        Log.d("Tag", "launch end")
    }
    

    并为 repo 的 updateData() 函数添加一些延迟:

    suspend fun updateData(): Result<Void> {
       delay(5000)
       val response = API.update().await()
       return response
    }
    

    而且,例如,在调用presenter.updateData() 之后的片段中,我们调用类似Handler().postDelayed({ presenter.stopUpdate() }, 3000) 的内容,我们不会在Logcat 中看到“launch end” 登录.

【讨论】:

  • 感谢您的详细解释。但我有一个疑问。如何指定网络调用应在 IO 上运行并在主线程上更新 ui。目前在我的实现中,我认为它不是那么可读
  • 通过将Dispatchers.Main指定为Coroutine Context,你表明协程是在主线程的上下文中启动的。所以你可以在那里运行你的 UI 登录(例如将测试设置为 TextView)。在协程中调用挂起函数repo.updateData(),表示协程在执行网络调用时被挂起而不阻塞主线程。
猜你喜欢
  • 2021-11-22
  • 2014-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多