Vince
Vince

Reputation: 1664

LiveData - Using SingleLiveEvent with Transformations

I am now using the recent Android Architecture Components and in particular the ViewModel and LiveData.

I am in a case where the SingleLiveEvent suggested here is relevant, i.e. I am returning an error and I want to display an alert only once. Before emitting a value to the Activity, I need to map my error to a more appropriate object for the view. I am using a Transformation for this.

So in the end, I have a ViewModel that looks like:

public LiveData<ViewState> getStateForView() {
    final LiveData<NetworkState> liveState = myRepository.getState();
    return Transformations.map(liveState, myMapper::map);
}

Where in my repository I am using a SingleLiveEvent:

public LiveData<NetworkState> getState() {
    myNetworkState = new SingleLiveEvent<>();
    return myNetworkState;
}

This works fairly well but I notice that my event is not propagated all the time when orientation changes multiple times. When debugging, I have noticed that there is no symmetry between the observer registration and removal:

debug of registration

debug of removal

What happens is that my initial observer is never removed from the observers of the SingleLiveEvent (so if orientation changes multiple times my SingleLiveEvent has multiple observers).

I cannot figure out why this is not the same observer at removal time. When reproducing it without a Transformation step, there is no such issue.

Does anyone have a hint about this behaviour? Are SingleLiveEvent (not part of the framework) and Transformations not supposed to work together?

Upvotes: 3

Views: 5199

Answers (1)

Vince
Vince

Reputation: 1664

I have found out that this is due to the fact that Transformations use a MediatorLiveData, which uses the SingleLiveEvent as a referenced source. This source is used for registration and removal of itself as an observer.

However, the SingleLiveEvent introduces an intermediate observer at registration time. The SingleLiveEvent references this intermediate observer and does not know about the MediatorLiveData.

At removal time, the MediatorLiveData tries to unregister itself as an observer from the SingleLiveEvent. Since the SingleLiveEvent does not know about it, it keeps the intermediate observer.

In the end, the process is not symmetrical and over time (as the user turns the phone), the SingleLiveEvent has more and more observers.

I don't know if I missed something or if the MediatorLiveData is not to be used with SingleLiveEvent, but I found a solution for my specific problem.

I added a reference to the intermediate observer in the SingleLiveEvent and I have overriden its removeObserver() method to remove the intermediate observer and not (only) the MediatorLiveData. I am not so confident about this solution because I am not familiar with the internals of the LiveData. In particular, this solution would only work if the SingleLiveEvent is used only with MediatorLiveData as observers (i.e. not if the Activity observes the SingleLiveEvent), and only if there is a single observer (which makes sense in the case of a SingleLiveEvent). There might be other limitations.

Here is the code I added to remove the right observer (singleLiveEventIntermediateObserver is the anonymous Observer instantiated in the observe() method):

@Override
public void removeObserver(@NonNull Observer<T> observer) {
    super.removeObserver(observer);
    if (this.singleLiveEventIntermediateObserver != null) {
        super.removeObserver(this.singleLiveEventIntermediateObserver);
    }
}

Upvotes: 3

Related Questions