I need to get some database objects from Room inside of a composable function. Currently when I try to call:
@Composable
fun loadDashboard() {
val db = DatabaseService.getDatabase(null)
val userDao = db.userDao()
val userModel = userDao.getOne()
}
I receive an error:
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
Is there a best practice solution for accessing the database in this situation so I can get data to make an API call?
CodePudding user response:
Best practice would be to create a repository class and do database operations there.
You can return a flow from the get method of the repository and emit the result when the operation is done. This flow can then be collected by a viewmodel inside a coroutine that is dispatched on the IO thread. When the result is collected, store it inside a state that is observed by the composable.
CodePudding user response:
You can change your userDao.getOne()
function to suspending function and call it from ViewModel inside a scope, ViewModel has viewModelScope by default, or your custom scope, this makes testing easy.
If you don't want to use ViewModel or repo, you can simply call this function inside LaunchedEffect
by calling your function in its lambda.
Or produceState
functions of Compose
Data with loading, success or error states with produceState for instance is as
@Composable
private fun getOneFromDb(): State<Result> {
return produceState<Result>(initialValue = Result.Loading) {
// In a coroutine, can make suspend calls
val one = db.getOne()
// Update State with either an Error or Success result.
// This will trigger a recomposition where this State is read
value = if (one == null) {
Result.Error
} else {
Result.Success(one)
}
}
}
val oneState = getOneFromDb()
sealed class Result {
object Loading : Result()
object Error : Result()
class Success(val one: OneModel) : Result()
}