I'm not super sure what I'm doing here so go easy on me:
Trying to retrieve a string from a pre-populated room database to my ViewModel and currently getting:
"StandaloneCoroutine{Active}@933049a"
instead of the actual data.
I have tried using LiveData which only returned null which as far as I'm aware is because it was not observed.
Switched to coroutines which seemed to make more sense if my UI doesn't need the data anyway. I ended up with this so far:
DAO:
@Dao
interface WordListDao {
@Query("SELECT word FROM wordlist WHERE used = 0 ORDER BY id DESC LIMIT 1")
suspend fun readWord(): String
// tried multiple versions here only string can be converted from Job
// @Query("SELECT * FROM wordlist WHERE used = 0 ORDER BY id DESC LIMIT 1")
// fun readWord(): LiveData<WordList>
// @Query("SELECT word FROM wordlist WHERE used = 0 ORDER BY id DESC LIMIT 1")
// fun readWord(): WordList
}
repository:
class WordRepository(private val wordListDao: WordListDao) {
//val readWordData: String = wordListDao.readWord()
suspend fun readWord(): String {
return wordListDao.readWord()
}
}
model:
@Entity(tableName = "wordlist")
data class WordList(
@PrimaryKey(autoGenerate = true)
val id: Int,
val word: String,
var used: Boolean
)
VM:
class HomeViewModel(application: Application) : ViewModel() {
private val repository: WordRepository
private var word: String
init {
val wordDb = WordListDatabase.getDatabase(application)
val wordDao = wordDb.wordlistDao()
repository = WordRepository(wordDao)
word = viewModelScope.launch {
repository.readWord()
}.toString()
Log.d("TAG", ": $word") // does nothing?
}
println(word) // StandaloneCoroutine{Active}@933049a
}
This is the only way that I have managed to not get the result of:
Cannot access database on the main thread
There is a better way to do this, I just can't figure it out.
CodePudding user response:
The return value is as expected, because launch does always return a Job
object representing the background process.
I do not know how you want to use the String
for, but all operations which should be done after receiving the String
must be moved inside the Coroutine
or in a function which is called from the Coroutine
.
viewModelScope.launch {
val word = repository.readWord()
// do stuff with word
// switch to MainThread if needed
launch(Dispatchers.Main){}
}
CodePudding user response:
You can access the return value of repository.readWord()
only inside the launch block.
viewModelScope.launch {
val word = repository.readWord()
Log.d("TAG", ": $word") // Here you will get the correct word
}
If you need to update you UI when this word is fetched from database, you need to use an observable data holder like a LiveData
or StateFlow
.
class HomeViewModel(application: Application) : ViewModel() {
private val repository: WordRepository
private val _wordFlow = MutableStateFlow("") // A mutable version for use inside ViewModel
val wordFlow = _word.asStateFlow() // An immutable version for outsiders to read this state
init {
val wordDb = WordListDatabase.getDatabase(application)
val wordDao = wordDb.wordlistDao()
repository = WordRepository(wordDao)
viewModelScope.launch {
_wordFlow.value = repository.readWord()
}
}
}
You can collect this Flow in your UI layer,
someCoroutineScope {
viewModel.wordFlow.collect { word ->
// Update UI using this word
}
}