Issue
Up until a few weeks ago, my LifeCycleOwnerAwareObserver class was working fine.
It was design with the purpose of self detach on Destroy.
@Override
public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
Log.d(TAG, "onStateChanged: event is: " + event.name());
Lifecycle.State state = source.getLifecycle().getCurrentState();
Log.println(Log.WARN, TAG, "onStateChanged: state is: " + state.name());
if (state.isAtLeast(INITIALIZED) && !state.equals(DESTROYED)) {
observer.get().accept(event);
} else if (state.equals(DESTROYED)) {
observer.get().accept(event);
observer.set(() -> null);
source.getLifecycle().removeObserver(this);
}
}
The idea was to build lifeCycle aware components to handle automatic unregistering.
90% of my project relies on this components...
I have not perceived any change, specially on Adapters, which listen to Fragments, the only a weird behavior I saw, where onViewCreated (an ON_START callback attaches an observer to the fragment's LifeCycleOwnerLiveData) was triggering slightly after the real onViewCreated(), but ONLY when coming back from the backStack... This is not good at all, but with some precautions it can be somewhat ignored.
But then this was the weirdest of them all...
I have a custom view (ViewParticle.class) with its own LifeCycle that implements a LifeCycleRegistry.
This code was working a few weeks ago... since I have not been testing everything constantly, I am not sure at which moment this stopped working here is the code:
private final MyLifeCycleOwner mViewLifecycleOwner = new MyLifeCycleOwner();
@Override
public void viewDestroyed() {
Lifecycle.Event event = Lifecycle.Event.ON_DESTROY;
Log.d(TAG, "viewDestroyed: event is: " + event.toString());
mViewLifecycleOwner.handleLifecycleEvent(event);
}
The receiving end:
@Override
public void viewPrepared() {
lifecycleSupplier = () -> mViewLifecycleOwner;
Lifecycle lCRef = mViewLifecycleOwner.getLifecycle();
//The callback HERE!!
lCRef.addObserver(
new LifeCycleOwnerAwareObserver(
event -> {
Log.d(TAG, "viewPrepared: event is: " + event.name());
if (event.equals(Lifecycle.Event.ON_DESTROY)) lastResponseProvider.run();
}
)
);
lifeCycleProvider.run();
mViewLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
mViewLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_START);
}
The Logs are showing this when viewDestroyed() is executed:
D/ViewParticle: viewDestroyed: event is: ON_DESTROY
D/MyLifeCycleOwner: handleLifecycleEvent: event is: ON_DESTROY
D/LifeCycleOwnerAwareObse: onStateChanged: event is: ON_STOP
W/LifeCycleOwnerAwareObse: onStateChanged: state is: DESTROYED
D/ViewParticle: viewPrepared: event is: ON_STOP
As you can see the Event.ON_DESTROY
enum is translating into:
a) Lifecycle.State.DESTROYED
b) Lifecycle.Event.ON_STOP
Which is impossible because the getStateAfter() method is as:
static State getStateAfter(Event event) {
switch (event) {
case ON_CREATE:
case ON_STOP:
return CREATED;
case ON_START:
case ON_PAUSE:
return STARTED;
case ON_RESUME:
return RESUMED;
case ON_DESTROY:
return DESTROYED;
case ON_ANY:
break;
}
throw new IllegalArgumentException("Unexpected event value " + event);
}
Which means an Event will never differ from an State, because an State IS the product of an Event, AND what triggers/begins the callback is the Event, NOT the STATE.
This means that if the State is Destroyed, the Event MUST be ON_DESTROYED.
I cannot explain what's happening here..
Solution
I won't look too much into the issue, but at first glance the answer seems to be here:
while (!isSynced()) {
mNewEventOccurred = false;
// no need to check eldest for nullability, because isSynced does it for us.
if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {
backwardPass(lifecycleOwner);
}
Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest();
if (!mNewEventOccurred && newest != null
&& mState.compareTo(newest.getValue().mState) > 0) {
forwardPass(lifecycleOwner);
}
}
The backwardPass(lifecycleOwner);
and forwardPass(lifecycleOwner);
methods, seem to work under the assumption that the mState.compareTo(newest.getValue().mState) > 0
and the mState.compareTo(mObserverMap.eldest().getValue().mState) < 0
conditions will never be greater than 1, so even if the answer is "true because it is 2", the forwardPass() method will only advance a single node in the life cycle, starting from the one of its previous value...
This behavior makes the function of the getStateAfter(Event event)
method pointless.
BTW I remember this specifically giving me problems on this line:
mViewLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
mViewLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_START);
Of my code above, this means that this in fact gave me problems before, So now the weird thing that needs clarification is why was it working fine in the first place? IDK.
The Answer is this:
@Override
public void viewDestroyed() {
Lifecycle.Event event = Lifecycle.Event.ON_STOP;
Log.d(TAG, "viewDestroyed: event is: " + event.toString());
mViewLifecycleOwner.handleLifecycleEvent(event);
Lifecycle.Event event2 = Lifecycle.Event.ON_DESTROY;
Log.d(TAG, "viewDestroyed: event is: " + event.toString());
mViewLifecycleOwner.handleLifecycleEvent(event2);
}
The requirement is that one needs to traverse the enums in order, up to the point of reaching what's desired.
public enum Event {
ON_CREATE,
ON_START,
ON_RESUME,
ON_PAUSE,
ON_STOP,
ON_DESTROY,
ON_ANY
}
It seems, as of now, that the only skippable enums are:
ON_RESUME
and ON_PAUSE
Answered By - Delark
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.