In my ViewModel I have a lateinit var to hold some LiveData. The way this variable is initialized depends on the data and the current date. Can't do it in SQL. This is the ViewModel:
class MainViewModel {
lateinit var timeStamps: LiveData<List<TimeStamp>>
init {
viewModelScope.launch {
val db = RoomDB.getInstance(application).timeStampDao()
val lastTimeStamp = db.getLast()
if (lastTimeStamp == null
|| (lastTimeStamp.instant < setToStartOfDay(Calendar.getInstance()).timeInMillis)
&& lastTimeStamp.action == ACTION.END_WORK) {
timeStamps = db.getAllAfterLive(Calendar.getInstance().timeInMillis)
} else {
db.getLastAction(ACTION.START_WORK)?.let { lastStartWork ->
val startOfDay = setToStartOfDay(initCalendar(lastStartWork.instant)).timeInMillis
db.getFirstActionAfter(ACTION.START_WORK, startOfDay)?.let {
timeStamps = db.getAllAfterLive(it.instant)
}
}
}
Here I access timeStamps in my Activity:
override fun onCreate(savedInstanceState: Bundle?) {
viewModel.timeStamps.observe(this) { list -> recordsAdapter.submitList(list) }
This leads to a UninitializedPropertyAccessException: onCreate runs faster than the timeStamps initialization launched in parallel.
I fixed this by introducing another lateinit var for a callback:
class MainViewModel {
lateinit var timeStamps: LiveData<List<TimeStamp>>
lateinit var timeStampsInitializedCallback: () -> Unit
init {
viewModelScope.launch {
// inspect the data and initialize timeStamps
timeStampsInitializedCallback()
}
which I initialize in onCreate:
override fun onCreate(savedInstanceState: Bundle?) {
viewModel.timeStampsInitializedCallback = {
viewModel.timeStamps.observe(this) { list -> recordsAdapter.submitList(list) }
}
This works, but it introduces a race condition. Should the initialization for timeStamps unexpectedly finish before the callback is initialized, I'd get another UninitializedPropertyAccessException and be back where I started.
How can I improve this code?
CodePudding user response:
You can also use liveData builder function:
class MainViewModel {
val timeStamps: LiveData<List<TimeStamp>> = liveData {
// inspect the data and initialize timeStamps
emit(timeStamps) // emit list of TimeStamps
emitSource(liveData) // emit another LiveData
}
}
// in Activity
override fun onCreate(savedInstanceState: Bundle?) {
viewModel.timeStamps.observe(this) { list -> recordsAdapter.submitList(list) }
}
The liveData code block starts executing when LiveData becomes active and is automatically canceled after a configurable timeout when the LiveData becomes inactive.
CodePudding user response:
The simplest option seems like MutableLiveData:
class MainViewModel {
private val _timeStamps = MutableLiveData<List<TimeStamp>>()
val timeStamps: LiveData<List<TimeStamp>> = _timeStamps
init {
viewModelScope.launch {
// inspect the data and set a value on _timeStamps
}
Depending on what the coroutine is doing, there may be other options (e.g., asLiveData() on a Flow, MediatorLiveData).
