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