【问题标题】:Android multithreading - coroutine and UI threadAndroid 多线程 - 协程和 UI 线程
【发布时间】:2019-12-02 10:34:48
【问题描述】:

我是多线程的新手,正在寻找解决这个问题的方法。 我正在协程中启动一个方法,它更新我的数据库中的数据,如果它更新了,我想为用户更新 UI。这个怎么办?我不能将 runOnUiThread 放在协程中。是否有某种类型的魔法,例如 -> 当协程完成时 -> 然后 -> runOnUi?

问候

【问题讨论】:

  • 也许观察数据库的更新并在更新后重新渲染 UI。带有 LiveData 的 Room 可以为您提供同一事物的 observables
  • 嗯,这是个好主意。会检查这个:)但是有没有解决方案等待在协程范围内运行的东西完成?
  • 任何没有 async 关键字的挂起函数都将在一个范围内按顺序运行,因此您可以放置​​两个函数,一个用于写入,另一个用于从 DB 中读取。
  • 看看这篇medium.com/androiddevelopers/…的文章在这里视图上的动画是按顺序完成的,也可以同时使用协程完成(编辑链接)
  • 谢谢 :) 这么新鲜的消息(今天写的 :D)

标签: android multithreading kotlin-coroutines


【解决方案1】:

您无需调用runOnUiThread,因为协程将以主调度程序作为上下文。

假设您有这个辅助函数可以将工作卸载到 I/O 线程。

suspend fun <T> withIO(block: suspend CoroutineScope.() -> T) = withContext(Dispatchers.IO, block)

如果您使用的是 ViewModel,那么您可以这样调用它

viewModelScope.launch {
    val result = withIO { 
        // You are on IO thread here.
        update your database
    }
    // The block will be suspended until the above task is done.
    // You are on UI thread now.
    // Update your UI.
}

如果你不使用 ViewModel,你也可以使用

withContext(Disptachers.Main) {
   val result = withIO {
       // You are on IO thread
   }
   // You are back on the main thread with the result from the task
}

【讨论】:

    【解决方案2】:

    协程是在不同线程上工作的任务。 你真正想要的是等待数据库的变化。这个想法中的协程可以在db中插入数据,但是监听部分是ViewModel模式的角色。

    我最近回答了你的类似问题:

    AutocompleteTextView with room

    更具体的可能是其他用户的回答:

    Wait until Kotlin coroutine finishes in onCreateView()

    【讨论】:

      【解决方案3】:

      所以基本问题是协程结束后跳回主线程

      这可以通过多种方式完成

      使用启动(Dispatcher.Main)

      从主线程初始化协程 像这样的

      //launches coroutine running on main thread
      GlobalScope.launch(Dispatchers.Main) {
          updateDb()
      }
      
      suspend fun updateDb(){
          //runs on worker thread and returns data
          val value = withContext(Dispatchers.IO){
             saveDataInDb();
          }
          //runs back on main thread
          updateUI(value);
      }
      

      但不应使用全局范围 你可以在这里阅读https://medium.com/@elizarov/the-reason-to-avoid-globalscope-835337445abc

      使用异步等待

      suspend fun saveInDb() {
          val value = GlobalScope.async {
              delay(1000)
              println("thread running on [${Thread.currentThread().name}]")
              10
          }
          println("value =  ${value.await()} thread running on [${Thread.currentThread().name}]")
      }
      

      输出:

       thread running on [DefaultDispatcher-worker-1]
       value =  10 thread running on [main]
       thread running on [main]
      

      【讨论】: