【问题标题】:Android Room library with Kotlin Flow toList() doesn't work带有 Kotlin Flow toList() 的 Android Room 库不起作用
【发布时间】:2020-06-21 13:17:36
【问题描述】:

我使用 Room 和 Flows 制作了一个简单的示例应用程序:

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    val build = Room.databaseBuilder(this, FinanceDatabase::class.java, "database.db")
            .fallbackToDestructiveMigration()
            .build()
    GlobalScope.launch {
        build.currencyDao().addCurrency(CurrencyLocalEntity(1))
        val toList = build.currencyDao().getAllCurrencies().toList()
        Log.d("test", "list - $toList")
    }
}
}

@Entity(tableName = "currency")
data class CurrencyLocalEntity(
        @PrimaryKey(autoGenerate = true)
        @ColumnInfo(name = "currencyId")
        var id: Int
) {
    constructor() : this(-1)
}

@Dao
interface CurrencyDao {

@Query("SELECT * FROM currency")
fun getAllCurrencies(): Flow<CurrencyLocalEntity>

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun addCurrency(currency: CurrencyLocalEntity)
}

@Database(entities = [CurrencyLocalEntity::class], version = 1)
abstract class FinanceDatabase : RoomDatabase() {
    abstract fun currencyDao(): CurrencyDao
}

我想在上面的代码中使用toList() 函数,但是出现了问题,甚至 Log 也无法打印。同时使用collect() 可以正常工作并给我所有记录。

任何人都可以向我解释什么是错的吗?谢谢。

【问题讨论】:

  • examples in the documentation 建议当您想从产生多个项目的查询中返回Flow 时,应使用Flow&lt;List&lt;T&gt;&gt;。返回Flow&lt;T&gt; 的示例用于返回单个项目的查询。您的 getAllCurrencies() 期望 Room 为 currency 表中的每个项目发出。我不认为它支持这一点。

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


【解决方案1】:

这里有一些问题,但我会解决主要问题。

房间返回的Flows 每次修改数据库时都会发出查询结果。 (这可能仅限于表更改而不是整个数据库)。

由于数据库可以在未来的任何时候更改,Flow 将(或多或少)永远不会完成,因为更改总是会发生。

您对返回的Flow 的调用toList() 将永远挂起,因为Flow 永远不会完成。这在概念上是有道理的,因为 Room 无法在不等待发生的情况下为您提供将要发生的所有更改的列表。 有了这个,我相信你知道为什么collect 给你记录而toList() 没有。

你可能想要的是这个。

@Query("SELECT * FROM currency")
fun getAllCurrencies(): Flow<List<CurrencyLocalEntity>>

这样你就可以得到Flow&lt;...&gt;.first()查询的第一个结果。

【讨论】:

  • 如果我们在 flow 中使用 first(),那么 Table changes 不会再次调用第一个 {} 块。但如果我们使用 collect {},那么任何 Table 更改都会调用 collect {}。您有什么方法可以让我们在第一个 {} 中继续获得表更改?
  • First 为您提供第一个更改。如果您想获得未来的更改,请使用 collect。
【解决方案2】:

Flow in Room 用于观察表格的变化。

每当对表进行任何更改时,无论更改哪一行,都会重新触发查询,并且 Flow 将再次发出。

然而,数据库的这种行为也意味着,如果我们更新一个不相关的行,我们的 Flow 将再次发出,结果相同。由于 SQLite 数据库触发器只允许在表级别而不是在行级别进行通知,Room 无法知道表数据中究竟发生了什么变化

【讨论】:

    【解决方案3】:

    确保用于检索列表的同一个 doa 对象用于更新数据库。 除了使用 asLivedata 扩展函数将流转换为 livedata 之外

    【讨论】:

      【解决方案4】:
      **getAllCurrencies()** function should be suspend.
      
      Please check the syntax to collect List from Flow: 
      
      suspend fun <T> Flow<T>.toList(
          destination: MutableList<T> = ArrayList()
      ): List<T> (source)
      
      https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/to-list.html
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-10-19
        • 1970-01-01
        • 1970-01-01
        • 2020-06-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多