LiveEvent: Observer registered after LiveEvent already has value is not executed

If the LiveEvent’s setValue is called before observe and there is no observer to consume the event the next registered observer is not receiving the event as well because pending is by default false.

This is maybe a deliberate decision but I find it a huge weakness of the design.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 21 (9 by maintainers)

Most upvoted comments

This is related to the rejected PR I posted a while back: https://github.com/hadilq/LiveEvent/pull/24

I still consider it to be a bug, especially given how many people continue to run into this situation. The emitter shouldn’t care about the order of observers but ¯\_(ツ)_/¯

Your test is clearly setting an event that should be observed in the future, which makes it being not-event!

I would disagree that it’s not an event in the definition of “single live event”. I would also argue that’s actually desirable to send an event for a future observer to see. Otherwise there is a coupling of the lifecycles between the observer and the emitter that I don’t think should exist. (The emitter has to know that there’s no observer yet and it’s not safe to emit.)

Say during view model initialization you want to show a toast, send a broadcast or a push notification or any other action that should happen just once. (Perhaps some early error condition was noticed.) It’s very conceivable that the view model could emit an event well before the observer has started to consume the events.

This situation can happen not only at view model initialization time but also during configuration change if things are timed just right. The observers stop observing and for a very brief moment it’s possible to emit a value before the new observers start observing.

The first condition, emitting during view model initialization, is “solved” by holding a pending event until the first observer consumes it. The second condition, emitting during a brief configuration change window, is not solvable without moving to something like channels.

@nikhiljainlive Thank you for sharing details. This problem happens because in init of VM no LiveData is obsereved yet. Personally I would post it as you did, but it may really be a problem. I need to think more guys.

@hadilq I am observing the LiveEvent in my Activity’s onCreate method only. Refer below for a detailed explanation of my issue. I have a ViewModel class in which there are two properties like shown below:

private val _currentUser = LiveEvent<CurrentUserState>()
val currentUser = _currentUser as LiveData<CurrentUserState>

And, in ViewModel init block, I am loading the user by calling the loadCurrentUser function from some data storage. This later on sets the Success event or Failure event when the user is loaded or any failure occurs. Refer to the code snippet shown below:

init {
    _currentUser.value = Loading
    viewModelScope.launch {
        loadCurrentUser()
    }
}

The currentUser Livedata is then observed at MainActivity onCreate method.

Issue: The problem is the Loading event is never emitted when I set the value to LiveEvent by calling _currentUser.value = Loading (setValue method). But when I set the value in LiveEvent by calling _currentUser.postValue(Loading)(postValue method), the observer receives the Loading value even if it was not registered when the value was posted. So, I see different behaviors when using setValue and postValue with the LiveEvent.

I also think this should be supported (that’s why I created the PR) but well… I understand the other points as well. Anyway post is working for you because it is asynchronous and your observer make it to register in the meantime. But I wouldn’t take this “luck” for granted as it can be unpredictable.

@hadilq I am observing the LiveEvent in my Activity’s onCreate method only. Refer below for a detailed explanation of my issue. I have a ViewModel class in which there are two properties like shown below:

private val _currentUser = LiveEvent<CurrentUserState>()
val currentUser = _currentUser as LiveData<CurrentUserState>

And, in ViewModel init block, I am loading the user by calling the loadCurrentUser function from some data storage. This later on sets the Success event or Failure event when the user is loaded or any failure occurs. Refer to the code snippet shown below:

init {
    _currentUser.value = Loading
    viewModelScope.launch {
        loadCurrentUser()
    }
}

The currentUser Livedata is then observed at MainActivity onCreate method.

Issue: The problem is the Loading event is never emitted when I set the value to LiveEvent by calling _currentUser.value = Loading (setValue method). But when I set the value in LiveEvent by calling _currentUser.postValue(Loading)(postValue method), the observer receives the Loading value even if it was not registered when the value was posted. So, I see different behaviors when using setValue and postValue with the LiveEvent.