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      */
LifecycleRegistry(@onNull LifecycleOwner provider)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
markState(@onNull State state)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      */
handleLifecycleEvent(@onNull Lifecycle.Event event)122     public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {
123         State next = getStateAfter(event);
124         moveToState(next);
125     }
126 
moveToState(State next)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 
isSynced()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 
calculateTargetState(LifecycleObserver observer)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
addObserver(@onNull LifecycleObserver observer)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 
popParentState()194     private void popParentState() {
195         mParentStates.remove(mParentStates.size() - 1);
196     }
197 
pushParentState(State state)198     private void pushParentState(State state) {
199         mParentStates.add(state);
200     }
201 
202     @Override
removeObserver(@onNull LifecycleObserver observer)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")
getObserverCount()225     public int getObserverCount() {
226         return mObserverMap.size();
227     }
228 
229     @NonNull
230     @Override
getCurrentState()231     public State getCurrentState() {
232         return mState;
233     }
234 
getStateAfter(Event event)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 
downEvent(State state)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 
upEvent(State state)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 
forwardPass(LifecycleOwner lifecycleOwner)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 
backwardPass(LifecycleOwner lifecycleOwner)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
sync()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 
min(@onNull State state1, @Nullable State state2)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 
ObserverWithState(LifecycleObserver observer, State initialState)347         ObserverWithState(LifecycleObserver observer, State initialState) {
348             mLifecycleObserver = Lifecycling.getCallback(observer);
349             mState = initialState;
350         }
351 
dispatchEvent(LifecycleOwner owner, Event event)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