Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I made a simple example app with using Room and 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
}

I want to use toList() function as in code above but something gets wrong and even Log doesn't print. At the same time using collect() works fine and gives me all records.

Can anybody explain to me what is wrong? Thanks.

like image 618
Ruslan Leshchenko Avatar asked Mar 03 '23 08:03

Ruslan Leshchenko


2 Answers

There are a couple things wrong here but I'll address the main issue.

Flows returned by room emit the result of the query everytime the database is modified. (This might be scoped to table changes instead of the whole database).

Since the database can change at any point in the future, the Flow will (more or less) never complete because a change can always happen.

Your calling toList() on the returned Flow will suspend forever, since the Flow never completes. This conceptually makes sense since Room cannot give you the list of every change that will happen, without waiting for it to happen. With this, I'm sure you know why collect gives you the records and toList() doesn't.

What you probably want here is this.

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

With this you can get the first result of the query with Flow<...>.first().

like image 127
Dominic Fischer Avatar answered Mar 05 '23 15:03

Dominic Fischer


Flow in Room is for observing Changes in table.

Whenever any changes are made to the table, independent of which row is changed, the query will be re-triggered and the Flow will emit again.

However, this behavior of the database also means that if we update an unrelated row, our Flow will emit again, with the same result. Because SQLite database triggers only allow notifications at table level and not at row level, Room can’t know what exactly has changed in the table data

like image 26
Omid Faraji Avatar answered Mar 05 '23 14:03

Omid Faraji