| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.arch.lifecycle; |
| |
| import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE; |
| import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY; |
| import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE; |
| import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME; |
| import static android.arch.lifecycle.Lifecycle.Event.ON_START; |
| import static android.arch.lifecycle.Lifecycle.Event.ON_STOP; |
| import static android.arch.lifecycle.Lifecycle.State.CREATED; |
| import static android.arch.lifecycle.Lifecycle.State.DESTROYED; |
| import static android.arch.lifecycle.Lifecycle.State.INITIALIZED; |
| import static android.arch.lifecycle.Lifecycle.State.RESUMED; |
| import static android.arch.lifecycle.Lifecycle.State.STARTED; |
| |
| import android.arch.core.internal.FastSafeIterableMap; |
| import android.support.annotation.NonNull; |
| import android.support.annotation.Nullable; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.Map.Entry; |
| |
| /** |
| * An implementation of {@link Lifecycle} that can handle multiple observers. |
| * <p> |
| * It is used by Fragments and Support Library Activities. You can also directly use it if you have |
| * a custom LifecycleOwner. |
| */ |
| public class LifecycleRegistry extends Lifecycle { |
| |
| /** |
| * Custom list that keeps observers and can handle removals / additions during traversal. |
| * |
| * Invariant: at any moment of time for observer1 & observer2: |
| * if addition_order(observer1) < addition_order(observer2), then |
| * state(observer1) >= state(observer2), |
| */ |
| private FastSafeIterableMap<LifecycleObserver, ObserverWithState> mObserverMap = |
| new FastSafeIterableMap<>(); |
| /** |
| * Current state |
| */ |
| private State mState; |
| /** |
| * The provider that owns this Lifecycle. |
| */ |
| private final LifecycleOwner mLifecycleOwner; |
| |
| private int mAddingObserverCounter = 0; |
| |
| private boolean mHandlingEvent = false; |
| private boolean mNewEventOccurred = false; |
| |
| // we have to keep it for cases: |
| // void onStart() { |
| // mRegistry.removeObserver(this); |
| // mRegistry.add(newObserver); |
| // } |
| // newObserver should be brought only to CREATED state during the execution of |
| // this onStart method. our invariant with mObserverMap doesn't help, because parent observer |
| // is no longer in the map. |
| private ArrayList<State> mParentStates = new ArrayList<>(); |
| |
| /** |
| * Creates a new LifecycleRegistry for the given provider. |
| * <p> |
| * You should usually create this inside your LifecycleOwner class's constructor and hold |
| * onto the same instance. |
| * |
| * @param provider The owner LifecycleOwner |
| */ |
| public LifecycleRegistry(@NonNull LifecycleOwner provider) { |
| mLifecycleOwner = provider; |
| mState = INITIALIZED; |
| } |
| |
| /** |
| * Only marks the current state as the given value. It doesn't dispatch any event to its |
| * listeners. |
| * |
| * @param state new state |
| */ |
| @SuppressWarnings("WeakerAccess") |
| public void markState(@NonNull State state) { |
| mState = state; |
| } |
| |
| /** |
| * Sets the current state and notifies the observers. |
| * <p> |
| * Note that if the {@code currentState} is the same state as the last call to this method, |
| * calling this method has no effect. |
| * |
| * @param event The event that was received |
| */ |
| public void handleLifecycleEvent(@NonNull Lifecycle.Event event) { |
| mState = getStateAfter(event); |
| if (mHandlingEvent || mAddingObserverCounter != 0) { |
| mNewEventOccurred = true; |
| // we will figure out what to do on upper level. |
| return; |
| } |
| mHandlingEvent = true; |
| sync(); |
| mHandlingEvent = false; |
| } |
| |
| private boolean isSynced() { |
| if (mObserverMap.size() == 0) { |
| return true; |
| } |
| State eldestObserverState = mObserverMap.eldest().getValue().mState; |
| State newestObserverState = mObserverMap.newest().getValue().mState; |
| return eldestObserverState == newestObserverState && mState == newestObserverState; |
| } |
| |
| private State calculateTargetState(LifecycleObserver observer) { |
| Entry<LifecycleObserver, ObserverWithState> previous = mObserverMap.ceil(observer); |
| |
| State siblingState = previous != null ? previous.getValue().mState : null; |
| State parentState = !mParentStates.isEmpty() ? mParentStates.get(mParentStates.size() - 1) |
| : null; |
| return min(min(mState, siblingState), parentState); |
| } |
| |
| @Override |
| public void addObserver(@NonNull LifecycleObserver observer) { |
| State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED; |
| ObserverWithState statefulObserver = new ObserverWithState(observer, initialState); |
| ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver); |
| |
| if (previous != null) { |
| return; |
| } |
| |
| boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent; |
| |
| State targetState = calculateTargetState(observer); |
| mAddingObserverCounter++; |
| while ((statefulObserver.mState.compareTo(targetState) < 0 |
| && mObserverMap.contains(observer))) { |
| pushParentState(statefulObserver.mState); |
| statefulObserver.dispatchEvent(mLifecycleOwner, upEvent(statefulObserver.mState)); |
| popParentState(); |
| // mState / subling may have been changed recalculate |
| targetState = calculateTargetState(observer); |
| } |
| |
| if (!isReentrance) { |
| // we do sync only on the top level. |
| sync(); |
| } |
| mAddingObserverCounter--; |
| } |
| |
| private void popParentState() { |
| mParentStates.remove(mParentStates.size() - 1); |
| } |
| |
| private void pushParentState(State state) { |
| mParentStates.add(state); |
| } |
| |
| @Override |
| public void removeObserver(@NonNull LifecycleObserver observer) { |
| // we consciously decided not to send destruction events here in opposition to addObserver. |
| // Our reasons for that: |
| // 1. These events haven't yet happened at all. In contrast to events in addObservers, that |
| // actually occurred but earlier. |
| // 2. There are cases when removeObserver happens as a consequence of some kind of fatal |
| // event. If removeObserver method sends destruction events, then a clean up routine becomes |
| // more cumbersome. More specific example of that is: your LifecycleObserver listens for |
| // a web connection, in the usual routine in OnStop method you report to a server that a |
| // session has just ended and you close the connection. Now let's assume now that you |
| // lost an internet and as a result you removed this observer. If you get destruction |
| // events in removeObserver, you should have a special case in your onStop method that |
| // checks if your web connection died and you shouldn't try to report anything to a server. |
| mObserverMap.remove(observer); |
| } |
| |
| /** |
| * The number of observers. |
| * |
| * @return The number of observers. |
| */ |
| @SuppressWarnings("WeakerAccess") |
| public int getObserverCount() { |
| return mObserverMap.size(); |
| } |
| |
| @Override |
| public State getCurrentState() { |
| return mState; |
| } |
| |
| 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); |
| } |
| |
| private static Event downEvent(State state) { |
| switch (state) { |
| case INITIALIZED: |
| throw new IllegalArgumentException(); |
| case CREATED: |
| return ON_DESTROY; |
| case STARTED: |
| return ON_STOP; |
| case RESUMED: |
| return ON_PAUSE; |
| case DESTROYED: |
| throw new IllegalArgumentException(); |
| } |
| throw new IllegalArgumentException("Unexpected state value " + state); |
| } |
| |
| private static Event upEvent(State state) { |
| switch (state) { |
| case INITIALIZED: |
| case DESTROYED: |
| return ON_CREATE; |
| case CREATED: |
| return ON_START; |
| case STARTED: |
| return ON_RESUME; |
| case RESUMED: |
| throw new IllegalArgumentException(); |
| } |
| throw new IllegalArgumentException("Unexpected state value " + state); |
| } |
| |
| private void forwardPass() { |
| Iterator<Entry<LifecycleObserver, ObserverWithState>> ascendingIterator = |
| mObserverMap.iteratorWithAdditions(); |
| while (ascendingIterator.hasNext() && !mNewEventOccurred) { |
| Entry<LifecycleObserver, ObserverWithState> entry = ascendingIterator.next(); |
| ObserverWithState observer = entry.getValue(); |
| while ((observer.mState.compareTo(mState) < 0 && !mNewEventOccurred |
| && mObserverMap.contains(entry.getKey()))) { |
| pushParentState(observer.mState); |
| observer.dispatchEvent(mLifecycleOwner, upEvent(observer.mState)); |
| popParentState(); |
| } |
| } |
| } |
| |
| private void backwardPass() { |
| Iterator<Entry<LifecycleObserver, ObserverWithState>> descendingIterator = |
| mObserverMap.descendingIterator(); |
| while (descendingIterator.hasNext() && !mNewEventOccurred) { |
| Entry<LifecycleObserver, ObserverWithState> entry = descendingIterator.next(); |
| ObserverWithState observer = entry.getValue(); |
| while ((observer.mState.compareTo(mState) > 0 && !mNewEventOccurred |
| && mObserverMap.contains(entry.getKey()))) { |
| Event event = downEvent(observer.mState); |
| pushParentState(getStateAfter(event)); |
| observer.dispatchEvent(mLifecycleOwner, event); |
| popParentState(); |
| } |
| } |
| } |
| |
| // happens only on the top of stack (never in reentrance), |
| // so it doesn't have to take in account parents |
| private void sync() { |
| 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(); |
| } |
| Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest(); |
| if (!mNewEventOccurred && newest != null |
| && mState.compareTo(newest.getValue().mState) > 0) { |
| forwardPass(); |
| } |
| } |
| mNewEventOccurred = false; |
| } |
| |
| static State min(@NonNull State state1, @Nullable State state2) { |
| return state2 != null && state2.compareTo(state1) < 0 ? state2 : state1; |
| } |
| |
| static class ObserverWithState { |
| State mState; |
| GenericLifecycleObserver mLifecycleObserver; |
| |
| ObserverWithState(LifecycleObserver observer, State initialState) { |
| mLifecycleObserver = Lifecycling.getCallback(observer); |
| mState = initialState; |
| } |
| |
| void dispatchEvent(LifecycleOwner owner, Event event) { |
| State newState = getStateAfter(event); |
| mState = min(mState, newState); |
| mLifecycleObserver.onStateChanged(owner, event); |
| mState = newState; |
| } |
| } |
| } |