【问题标题】:Room Coroutine is executed in main threadRoom Coroutine 在主线程中执行
【发布时间】:2020-10-09 13:54:46
【问题描述】:

我在玩 Kotlin Flow、Coroutines 和 Room。我正在尝试将数据从我的存储库插入到我的房间数据库中。

插入单个项目时没有问题,但如果我尝试插入项目列表,则执行中止并且控制台记录以下消息:

I/Choreographer: Skipped 47 frames!  The application may be doing too much work on its main thread.

我不明白为什么插入操作在主线程上执行,因为根据文档,暂停的 DAO 操作应该始终在 Room 内部启动的协程中执行(最终应该在后台线程上运行)。我还尝试在另一个 Scope 中显式运行插入调用(withContext(Dispatchers.IO) { ... }),但没有区别。

我的代码如下所示:

视图模型:

fun setStateEvent(stateEvent: StateEvent) {
    viewModelScope.launch {
        when (stateEvent) {
            is StateEvent.GetItems -> {
                repository.getItems().onEach { dateState ->
                    _dataState.value = dateState
                }.launchIn(viewModelScope)
            }
        }
    }
}

存储库:

suspend fun getItems(): Flow<DataState<List<Item>>> = flow {
    emit(DataState.Loading)
    try {
        val items = itemService.getAllItems()
        emit(DataState.Success(items))
        itemDao.insertItems(items) // The execution stops here
    } catch (e: Exception) {
        emit(DataState.Error(e))
    }
}

道:

@Insert
suspend fun insertItems(items: List<Item>)

我也尝试调试并找到问题的根源,但我没有运气。如果有人能告诉我出了什么问题,我会很高兴。

【问题讨论】:

    标签: android kotlin android-room kotlin-coroutines android-mvvm


    【解决方案1】:

    在我看来,这里没有问题。 Choreographer messages can be misleading,可能与主线程无关。我在调试时经常看到这种情况,可能是由于调试器的开销。大量布局的膨胀也可能导致它。

    您也不需要launchIn(viewModelScope),因为流已经在该范围内收集。只需使用collect()。与其他答案可能暗示的不同,您说的对,当使用 suspend DAO 方法时,Room 会自动切换线程。

    【讨论】:

    • 但该消息仅在调用insertItems 函数时才会出现。此外,我确信执行会以某种方式中断,因为项目没有被持久化,而且,如果我切换 emitinsertItems 函数调用并在 emit 语句上设置断点,断点永远不会到达,因此在尝试持久化数据时必须中断执行。顺便说一句,感谢.collect() 的提示!
    【解决方案2】:

    对不起,伙计们...我原始帖子中的代码没有问题。 我没有评估捕获异常时抛出的错误。所以问题是完全不同的(NOT NULL 约束失败)。

    因为主线程的消息,我没有考虑这个。

    感谢大家的帮助。

    【讨论】:

      【解决方案3】:

      您可以在执行 viewmodelScope 的 CoroutineScope 的启动中添加 Dispatchers.IO

      fun setStateEvent(stateEvent: StateEvent) {
          viewModelScope.launch(Dispatchers.IO) {
              when (stateEvent) {
                  is StateEvent.GetItems -> {
                      repository.getItems().collect {
                      for(item in it) { _dataState.value = item }
                     }
                  }
              }
          }
      }
      

      确保你的 gradle 中有 room-ktx 的依赖,这将允许你在你的界面中使用挂起函数与房间

      implementation "androidx.room:room-ktx:2.2.5"
      

      见:https://developer.android.com/jetpack/androidx/releases/room#declaring_dependencies

      无论哪种方式,您都无法从房间界面中的暂停中受益。

      【讨论】:

      • repository.getItems() 在使用您建议的代码时永远不会被调用。如果在.onEach() 之后调用.collect(),则可以修复此问题。尽管如此,还是会出现以下错误:java.lang.IllegalStateException: Cannot invoke setValue on a background thread。已添加依赖项。为什么我无法从暂停中受益?
      • 里面collect里面做dataState.value,我改了代码,看一下
      • 同样的错误。 _dataState 的类型为MutableLiveData,并且必须在主线程上调用 setValue 方法。编辑:现在看到你的编辑,我会看看你的代码,谢谢! EDIT2:这不会产生影响,因为它仍然会在 IO 线程上调用,并且 setValue 必须从主线程调用。
      猜你喜欢
      • 2023-03-04
      • 2017-06-19
      • 2020-02-23
      • 1970-01-01
      • 2023-03-05
      • 2011-08-05
      • 2015-10-27
      • 2023-04-05
      • 1970-01-01
      相关资源
      最近更新 更多