Reputation: 2025
I am using Room in my project and I want to get list from the DB but I get null although anytime I don't use live data, I get the values so I know the data is available in the database.
@Dao
interface AddressDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(address: Address): Long
@Query("SELECT * from address")
fun getAll(): LiveData<MutableList<Address>>
}
@Entity(tableName = "address")
data class Address(
@PrimaryKey(autoGenerate = true)
val id: Long = 0L)
class AddressDbRepository @Inject constructor(
private val addressDao: AddressDao
) {
suspend fun insertAddress(address: Address): Long = addressDao.insert(address = address)
fun getAll(): LiveData<MutableList<Address>> = addressDao.getAll()
}
@HiltViewModel
class AddressViewModel @Inject constructor(
private val addressDbRepository: AddressDbRepository,
@DefaultDispatcher private val defaultDispatcher: CoroutineDispatcher,
): ViewModel() {
private var _events2 = MutableLiveData<MutableList<Address>>()
val events2: LiveData<MutableList<Address>> = _events2
fun getAddress() {
job?.cancel()
val handler = CoroutineExceptionHandler { _, throwable ->
val message = when (throwable) {
is HttpException -> throwable.toErrorMessage()
else -> "An Error Occurred."
}
Log.d(
"PLACE_SEARCH_VIEWMODEL ERROR",
"PLACE_SEARCH_VIEWMODEL RESPONSE ERROR ${throwable.localizedMessage}"
)
Log.d(
"PLACE_SEARCH_VIEWMODEL ERROR",
"PLACE_SEARCH_VIEWMODEL RESPONSE ERROR ${message}"
)
}
job = viewModelScope.launch(handler) {
val resp = withContext(defaultDispatcher) {
addressDbRepository.getAll()
}
_events2.value = resp.value
Log.d("PLACE_ADDY_VIEWMODEL", "PLACE_ADDY_VIEWMODEL RESPONSE ${resp}")
}
}
}
Any help as to how I can get the value would be appreciated.
Upvotes: 2
Views: 479
Reputation: 93609
Looking at this code:
job = viewModelScope.launch(handler) {
val resp = withContext(defaultDispatcher) {
addressDbRepository.getAll()
}
_events2.value = resp.value
Log.d("PLACE_ADDY_VIEWMODEL", "PLACE_ADDY_VIEWMODEL RESPONSE ${resp}")
}
First of all, you don't need to specify a dispatcher to call getAll()
because it instantly returns a LiveData without blocking.
When you check its value
property immediately after it's created, it will not have received its first value yet because it is doing an asynchronous IO operation under the hood to retrieve that first value.
A LiveData is kind of inconvenient to use with a coroutine, as it has no mechanism to wait for its first result. You could write an extension function like this (note the final comment on that answer however--I think you need to get a main handler and post the code in the cancellation block), or you can cheat and turn it into a Kotlin Flow and await the first result by calling asFlow().first()
on it. Alternatively, you could make the source function return Flow instead of LiveData to take one step out.
You also might consider changing your function into a suspend
function that returns MutableList<Address>
directly if you don't need to observe this value for changes elsewhere. The point of having a DAO function that returns a LiveData or Flow is so you can subscribe to it and get updates every time the database changes. When you want to fetch the current value one time, a suspend function is more appropriate. But if you have need of both, the flow.first()
method is fine and avoids having to create multiple version of your DAO getters.
Upvotes: 1