King
King

Reputation: 2025

Fetch all with LiveData Room Kotlin

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

Answers (1)

Tenfour04
Tenfour04

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

Related Questions