Home | History | Annotate | Download | only in lifecycle
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package androidx.lifecycle;
     18 
     19 import static androidx.lifecycle.Lifecycle.Event.ON_CREATE;
     20 import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY;
     21 import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
     22 import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
     23 import static androidx.lifecycle.Lifecycle.Event.ON_START;
     24 import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
     25 import static androidx.lifecycle.Lifecycle.State.CREATED;
     26 import static androidx.lifecycle.Lifecycle.State.DESTROYED;
     27 import static androidx.lifecycle.Lifecycle.State.INITIALIZED;
     28 import static androidx.lifecycle.Lifecycle.State.RESUMED;
     29 import static androidx.lifecycle.Lifecycle.State.STARTED;
     30 
     31 import android.util.Log;
     32 
     33 import androidx.annotation.MainThread;
     34 import androidx.annotation.NonNull;
     35 import androidx.annotation.Nullable;
     36 import androidx.arch.core.internal.FastSafeIterableMap;
     37 
     38 import java.lang.ref.WeakReference;
     39 import java.util.ArrayList;
     40 import java.util.Iterator;
     41 import java.util.Map.Entry;
     42 
     43 /**
     44  * An implementation of {@link Lifecycle} that can handle multiple observers.
     45  * <p>
     46  * It is used by Fragments and Support Library Activities. You can also directly use it if you have
     47  * a custom LifecycleOwner.
     48  */
     49 public class LifecycleRegistry extends Lifecycle {
     50 
     51     private static final String LOG_TAG = "LifecycleRegistry";
     52 
     53     /**
     54      * Custom list that keeps observers and can handle removals / additions during traversal.
     55      *
     56      * Invariant: at any moment of time for observer1 & observer2:
     57      * if addition_order(observer1) < addition_order(observer2), then
     58      * state(observer1) >= state(observer2),
     59      */
     60     private FastSafeIterableMap<LifecycleObserver, ObserverWithState> mObserverMap =
     61             new FastSafeIterableMap<>();
     62     /**
     63      * Current state
     64      */
     65     private State mState;
     66     /**
     67      * The provider that owns this Lifecycle.
     68      * Only WeakReference on LifecycleOwner is kept, so if somebody leaks Lifecycle, they won't leak
     69      * the whole Fragment / Activity. However, to leak Lifecycle object isn't great idea neither,
     70      * because it keeps strong references on all other listeners, so you'll leak all of them as
     71      * well.
     72      */
     73     private final WeakReference<LifecycleOwner> mLifecycleOwner;
     74 
     75     private int mAddingObserverCounter = 0;
     76 
     77     private boolean mHandlingEvent = false;
     78     private boolean mNewEventOccurred = false;
     79 
     80     // we have to keep it for cases:
     81     // void onStart() {
     82     //     mRegistry.removeObserver(this);
     83     //     mRegistry.add(newObserver);
     84     // }
     85     // newObserver should be brought only to CREATED state during the execution of
     86     // this onStart method. our invariant with mObserverMap doesn't help, because parent observer
     87     // is no longer in the map.
     88     private ArrayList<State> mParentStates = new ArrayList<>();
     89 
     90     /**
     91      * Creates a new LifecycleRegistry for the given provider.
     92      * <p>
     93      * You should usually create this inside your LifecycleOwner class's constructor and hold
     94      * onto the same instance.
     95      *
     96      * @param provider The owner LifecycleOwner
     97      */
     98     public LifecycleRegistry(@NonNull LifecycleOwner provider) {
     99         mLifecycleOwner = new WeakReference<>(provider);
    100         mState = INITIALIZED;
    101     }
    102 
    103     /**
    104      * Moves the Lifecycle to the given state and dispatches necessary events to the observers.
    105      *
    106      * @param state new state
    107      */
    108     @SuppressWarnings("WeakerAccess")
    109     @MainThread
    110     public void markState(@NonNull State state) {
    111         moveToState(state);
    112     }
    113 
    114     /**
    115      * Sets the current state and notifies the observers.
    116      * <p>
    117      * Note that if the {@code currentState} is the same state as the last call to this method,
    118      * calling this method has no effect.
    119      *
    120      * @param event The event that was received
    121      */
    122     public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {
    123         State next = getStateAfter(event);
    124         moveToState(next);
    125     }
    126 
    127     private void moveToState(State next) {
    128         if (mState == next) {
    129             return;
    130         }
    131         mState = next;
    132         if (mHandlingEvent || mAddingObserverCounter != 0) {
    133             mNewEventOccurred = true;
    134             // we will figure out what to do on upper level.
    135             return;
    136         }
    137         mHandlingEvent = true;
    138         sync();
    139         mHandlingEvent = false;
    140     }
    141 
    142     private boolean isSynced() {
    143         if (mObserverMap.size() == 0) {
    144             return true;
    145         }
    146         State eldestObserverState = mObserverMap.eldest().getValue().mState;
    147         State newestObserverState = mObserverMap.newest().getValue().mState;
    148         return eldestObserverState == newestObserverState && mState == newestObserverState;
    149     }
    150 
    151     private State calculateTargetState(LifecycleObserver observer) {
    152         Entry<LifecycleObserver, ObserverWithState> previous = mObserverMap.ceil(observer);
    153 
    154         State siblingState = previous != null ? previous.getValue().mState : null;
    155         State parentState = !mParentStates.isEmpty() ? mParentStates.get(mParentStates.size() - 1)
    156                 : null;
    157         return min(min(mState, siblingState), parentState);
    158     }
    159 
    160     @Override
    161     public void addObserver(@NonNull LifecycleObserver observer) {
    162         State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
    163         ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
    164         ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
    165 
    166         if (previous != null) {
    167             return;
    168         }
    169         LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
    170         if (lifecycleOwner == null) {
    171             // it is null we should be destroyed. Fallback quickly
    172             return;
    173         }
    174 
    175         boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent;
    176         State targetState = calculateTargetState(observer);
    177         mAddingObserverCounter++;
    178         while ((statefulObserver.mState.compareTo(targetState) < 0
    179                 && mObserverMap.contains(observer))) {
    180             pushParentState(statefulObserver.mState);
    181             statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState));
    182             popParentState();
    183             // mState / subling may have been changed recalculate
    184             targetState = calculateTargetState(observer);
    185         }
    186 
    187         if (!isReentrance) {
    188             // we do sync only on the top level.
    189             sync();
    190         }
    191         mAddingObserverCounter--;
    192     }
    193 
    194     private void popParentState() {
    195         mParentStates.remove(mParentStates.size() - 1);
    196     }
    197 
    198     private void pushParentState(State state) {
    199         mParentStates.add(state);
    200     }
    201 
    202     @Override
    203     public void removeObserver(@NonNull LifecycleObserver observer) {
    204         // we consciously decided not to send destruction events here in opposition to addObserver.
    205         // Our reasons for that:
    206         // 1. These events haven't yet happened at all. In contrast to events in addObservers, that
    207         // actually occurred but earlier.
    208         // 2. There are cases when removeObserver happens as a consequence of some kind of fatal
    209         // event. If removeObserver method sends destruction events, then a clean up routine becomes
    210         // more cumbersome. More specific example of that is: your LifecycleObserver listens for
    211         // a web connection, in the usual routine in OnStop method you report to a server that a
    212         // session has just ended and you close the connection. Now let's assume now that you
    213         // lost an internet and as a result you removed this observer. If you get destruction
    214         // events in removeObserver, you should have a special case in your onStop method that
    215         // checks if your web connection died and you shouldn't try to report anything to a server.
    216         mObserverMap.remove(observer);
    217     }
    218 
    219     /**
    220      * The number of observers.
    221      *
    222      * @return The number of observers.
    223      */
    224     @SuppressWarnings("WeakerAccess")
    225     public int getObserverCount() {
    226         return mObserverMap.size();
    227     }
    228 
    229     @NonNull
    230     @Override
    231     public State getCurrentState() {
    232         return mState;
    233     }
    234 
    235     static State getStateAfter(Event event) {
    236         switch (event) {
    237             case ON_CREATE:
    238             case ON_STOP:
    239                 return CREATED;
    240             case ON_START:
    241             case ON_PAUSE:
    242                 return STARTED;
    243             case ON_RESUME:
    244                 return RESUMED;
    245             case ON_DESTROY:
    246                 return DESTROYED;
    247             case ON_ANY:
    248                 break;
    249         }
    250         throw new IllegalArgumentException("Unexpected event value " + event);
    251     }
    252 
    253     private static Event downEvent(State state) {
    254         switch (state) {
    255             case INITIALIZED:
    256                 throw new IllegalArgumentException();
    257             case CREATED:
    258                 return ON_DESTROY;
    259             case STARTED:
    260                 return ON_STOP;
    261             case RESUMED:
    262                 return ON_PAUSE;
    263             case DESTROYED:
    264                 throw new IllegalArgumentException();
    265         }
    266         throw new IllegalArgumentException("Unexpected state value " + state);
    267     }
    268 
    269     private static Event upEvent(State state) {
    270         switch (state) {
    271             case INITIALIZED:
    272             case DESTROYED:
    273                 return ON_CREATE;
    274             case CREATED:
    275                 return ON_START;
    276             case STARTED:
    277                 return ON_RESUME;
    278             case RESUMED:
    279                 throw new IllegalArgumentException();
    280         }
    281         throw new IllegalArgumentException("Unexpected state value " + state);
    282     }
    283 
    284     private void forwardPass(LifecycleOwner lifecycleOwner) {
    285         Iterator<Entry<LifecycleObserver, ObserverWithState>> ascendingIterator =
    286                 mObserverMap.iteratorWithAdditions();
    287         while (ascendingIterator.hasNext() && !mNewEventOccurred) {
    288             Entry<LifecycleObserver, ObserverWithState> entry = ascendingIterator.next();
    289             ObserverWithState observer = entry.getValue();
    290             while ((observer.mState.compareTo(mState) < 0 && !mNewEventOccurred
    291                     && mObserverMap.contains(entry.getKey()))) {
    292                 pushParentState(observer.mState);
    293                 observer.dispatchEvent(lifecycleOwner, upEvent(observer.mState));
    294                 popParentState();
    295             }
    296         }
    297     }
    298 
    299     private void backwardPass(LifecycleOwner lifecycleOwner) {
    300         Iterator<Entry<LifecycleObserver, ObserverWithState>> descendingIterator =
    301                 mObserverMap.descendingIterator();
    302         while (descendingIterator.hasNext() && !mNewEventOccurred) {
    303             Entry<LifecycleObserver, ObserverWithState> entry = descendingIterator.next();
    304             ObserverWithState observer = entry.getValue();
    305             while ((observer.mState.compareTo(mState) > 0 && !mNewEventOccurred
    306                     && mObserverMap.contains(entry.getKey()))) {
    307                 Event event = downEvent(observer.mState);
    308                 pushParentState(getStateAfter(event));
    309                 observer.dispatchEvent(lifecycleOwner, event);
    310                 popParentState();
    311             }
    312         }
    313     }
    314 
    315     // happens only on the top of stack (never in reentrance),
    316     // so it doesn't have to take in account parents
    317     private void sync() {
    318         LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
    319         if (lifecycleOwner == null) {
    320             Log.w(LOG_TAG, "LifecycleOwner is garbage collected, you shouldn't try dispatch "
    321                     + "new events from it.");
    322             return;
    323         }
    324         while (!isSynced()) {
    325             mNewEventOccurred = false;
    326             // no need to check eldest for nullability, because isSynced does it for us.
    327             if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {
    328                 backwardPass(lifecycleOwner);
    329             }
    330             Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest();
    331             if (!mNewEventOccurred && newest != null
    332                     && mState.compareTo(newest.getValue().mState) > 0) {
    333                 forwardPass(lifecycleOwner);
    334             }
    335         }
    336         mNewEventOccurred = false;
    337     }
    338 
    339     static State min(@NonNull State state1, @Nullable State state2) {
    340         return state2 != null && state2.compareTo(state1) < 0 ? state2 : state1;
    341     }
    342 
    343     static class ObserverWithState {
    344         State mState;
    345         GenericLifecycleObserver mLifecycleObserver;
    346 
    347         ObserverWithState(LifecycleObserver observer, State initialState) {
    348             mLifecycleObserver = Lifecycling.getCallback(observer);
    349             mState = initialState;
    350         }
    351 
    352         void dispatchEvent(LifecycleOwner owner, Event event) {
    353             State newState = getStateAfter(event);
    354             mState = min(mState, newState);
    355             mLifecycleObserver.onStateChanged(owner, event);
    356             mState = newState;
    357         }
    358     }
    359 }
    360