Reputation: 1664
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:
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
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