Android 带有 Kotlin Flow toList() 的 Room 库不工作

Android Room library with Kotlin Flow toList() doesn't work

我使用 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() 工作正常并给我所有记录。

谁能给我解释一下哪里出了问题?谢谢

Flow in Room 用于观察 table 的变化。

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

然而,数据库的这种行为也意味着如果我们更新一个不相关的行,我们的 Flow 将再次发出,结果相同。因为 SQLite 数据库触发器只允许 table 级别的通知而不是行级别的通知,Room 无法知道 table 数据

中到底发生了什么变化
**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

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

room 返回的

Flows 会在每次修改数据库时发出查询结果。 (这可能适用于 table 更改而不是整个数据库)。

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

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

你可能想要的是这个。

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

这样你就可以得到第一个查询结果 Flow<...>.first().

确保您用于检索列表的 doa 对象用于更新数据库。 除了使用 asLivedata 扩展函数

将流转换为实时数据之外