Home > Blockchain >  lateinit var for LiveData
lateinit var for LiveData

Time:06-11

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

  • Related