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
).