使用协程流程->收集时房间冻结

Posted

技术标签:

【中文标题】使用协程流程->收集时房间冻结【英文标题】:Room freeze when use coroutine flow->collect 【发布时间】:2020-03-07 03:12:12 【问题描述】:

我正在使用 Room (v. 2.2.1) 和协程支持 (v. 1.3.2) 并遵循设置

@Entity(tableName = "simple_table")
data class SimpleEntity(
    @PrimaryKey(autoGenerate = true) val id: Long = 0
) 

@Dao
interface SimpleDao 
    @Query("SELECT * FROM simple_table WHERE id = :id")
    fun getRecord(id: Long): Flow<SimpleEntity?>

    @Insert
    suspend fun insert(entity: SimpleEntity)

然后,在我将数据插入到 simple_table 中,自动生成的 id 为 1 之后,我尝试查询

db.simpleDao.getRecord(1).collect 
  //fetch data

db.close()

永远无法联系到db.close()。它只是冻结在collect() 方法中。如果我将代码更改为

db.simpleDao.getRecord(1).first 
  //fetch data

db.simpleDao.getRecord(1).take(1).collect 
  //fetch data

然后它可以正常工作。但是为什么普通的collect() 永远不会结束他们的执行?我在 Room 配置中遗漏了什么以使其正常工作?

【问题讨论】:

这闻起来像一个库错误。 【参考方案1】:

改为声明你的论点。 我检查了 first()take() 函数(运行良好)并与 count() 进行比较(不起作用)。区别在于参数。

尝试改变:

flow.collect 
   //fetch data

收件人:

flow.collect  value ->
   //fetch data

我不知道为什么,但这对我有用。

编辑:

first()take() 函数抛出 AbortFlowException()。试试这个:

try 
   db.simpleDao.getRecord(1).collect 
      //fetch data
      throw AbortFlowException()
   
 catch (e: AbortFlowException) 
   // Do nothing

db.close()

【讨论】:

Flow 的想法是从后台线程接收一次数据,然后在发生写入时再次接收。不需要使用take(1)first() 等,实际上这与使用Flow 时所期望的相反。 “声明你的论点而不是它”。这根本没有意义。如果有错误,它要么在 Room 中,要么在 Flow 中,但不在 Kotlin 中。 @Nicolas 这不是错误。这是正确的流动行为。请参阅“编辑”部分,AbortFlowException() 在这种情况下效果很好,并且在first()take() 功能中也是如此。 我同意你的观点,但不同意其他观点。我看到你把它划掉了。 AbortFlowException 是内部异常,无法从代码访问【参考方案2】:

我遇到了同样的问题,已经解决了

.collect 之后的代码在同一个协程中不起作用! 如果你这样写:

    MainScope().launch 

        db.simpleDao.getRecord(1).collect 
            //fetch data
        
        /**
         * My other code that will be freezed
         * */

    

你应该这样写:

    MainScope().launch 

        launch  //new coroutine
            db.simpleDao.getRecord(1).collect 
                //fetch data
            
            // No code here
        
        /**
         * My other code that will not be freezed
         * */

    

我认为这完全是因为 slot.awaitPending() 被调用,等待收集新值。

【讨论】:

以上是关于使用协程流程->收集时房间冻结的主要内容,如果未能解决你的问题,请参考以下文章

相机冻结:如何重新初始化相机组件?

从 iOS 收听 Kotlin 协程流程

协程流程:不确定如何将游标转换为此方法的返回类型

Python控制流程生成器 | 协程 | 期物 | 任务对比与联系

G1 GC垃圾收集流程

使用带有房间的 kotlin 协程