Hanbit the Developer
Android Lifecycle 분석 본문
서론
Activity, Fragment에서 Lifecycle이 어떻게 동작하는지 top-down으로 알아본다.
Activity, Fragment가 어떻게 Lifecycle을 갖는가?
ComponentActivity
, Fragment
는 LifecycleOwner
라는 인터페이스를 구현한다.
// ComponentActivity.java
/**
* Base class for activities that enables composition of higher level components.
* <p>
* Rather than all functionality being built directly into this class, only the minimal set of
* lower level building blocks are included. Higher level components can then be used as needed
* without enforcing a deep Activity class hierarchy or strong coupling between components.
*/
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
ContextAware,
LifecycleOwner,
ViewModelStoreOwner,
HasDefaultViewModelProviderFactory,
SavedStateRegistryOwner,
OnBackPressedDispatcherOwner,
ActivityResultRegistryOwner,
ActivityResultCaller,
OnConfigurationChangedProvider,
OnTrimMemoryProvider,
OnNewIntentProvider,
OnMultiWindowModeChangedProvider,
OnPictureInPictureModeChangedProvider,
MenuHost {
// ...
}
// Fragment.java
/**
* Static library support version of the framework's {@link android.app.Fragment}.
* Used to write apps that run on platforms prior to Android 3.0. When running
* on Android 3.0 or above, this implementation is still used; it does not try
* to switch to the framework's implementation. See the framework {@link android.app.Fragment}
* documentation for a class overview.
*
* <p>The main differences when using this support version instead of the framework version are:
* <ul>
* <li>Your activity must extend {@link FragmentActivity}
* <li>You must call {@link FragmentActivity#getSupportFragmentManager} to get the
* {@link FragmentManager}
* </ul>
*
*/
public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener, LifecycleOwner,
ViewModelStoreOwner, HasDefaultViewModelProviderFactory, SavedStateRegistryOwner,
ActivityResultCaller {
// ...
}
LifecycleOwner
는 getLifecycle() 함수를 정의하고 있다.
// LifecycleOwner.java
/**
* A class that has an Android lifecycle. These events can be used by custom components to
* handle lifecycle changes without implementing any code inside the Activity or the Fragment.
*
* @see Lifecycle
* @see ViewTreeLifecycleOwner
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public interface LifecycleOwner {
/**
* Returns the Lifecycle of the provider.
*
* @return The lifecycle of the provider.
*/
@NonNull
Lifecycle getLifecycle();
}
다시 ComponentActivity
, Fragment
로 돌아와서, getLifecycle()
의 구현을 보면 LifecycleRegistry
라는 클래스의 인스턴스를 Lifecycle
로서 반환하는 것을 확인할 수 있다.(DIP 원칙)
// ComponentActivity.java
/**
* Base class for activities that enables composition of higher level components.
* <p>
* Rather than all functionality being built directly into this class, only the minimal set of
* lower level building blocks are included. Higher level components can then be used as needed
* without enforcing a deep Activity class hierarchy or strong coupling between components.
*/
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
ContextAware,
LifecycleOwner,
ViewModelStoreOwner,
HasDefaultViewModelProviderFactory,
SavedStateRegistryOwner,
OnBackPressedDispatcherOwner,
ActivityResultRegistryOwner,
ActivityResultCaller,
OnConfigurationChangedProvider,
OnTrimMemoryProvider,
OnNewIntentProvider,
OnMultiWindowModeChangedProvider,
OnPictureInPictureModeChangedProvider,
MenuHost {
// ...
private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
// ...
/**
* {@inheritDoc}
* <p>
* Overriding this method is no longer supported and this method will be made
* <code>final</code> in a future version of ComponentActivity. If you do override
* this method, you <code>must</code>:
* <ol>
* <li>Return an instance of {@link LifecycleRegistry}</li>
* <li>Lazily initialize your LifecycleRegistry object when this is first called.
* Note that this method will be called in the super classes' constructor, before any
* field initialization or object state creation is complete.</li>
* </ol>
*/
@NonNull
@Override
public Lifecycle getLifecycle() {
return mLifecycleRegistry;
}
// ...
}
// Fragment.java
/**
* Static library support version of the framework's {@link android.app.Fragment}.
* Used to write apps that run on platforms prior to Android 3.0. When running
* on Android 3.0 or above, this implementation is still used; it does not try
* to switch to the framework's implementation. See the framework {@link android.app.Fragment}
* documentation for a class overview.
*
* <p>The main differences when using this support version instead of the framework version are:
* <ul>
* <li>Your activity must extend {@link FragmentActivity}
* <li>You must call {@link FragmentActivity#getSupportFragmentManager} to get the
* {@link FragmentManager}
* </ul>
*
*/
public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener, LifecycleOwner,
ViewModelStoreOwner, HasDefaultViewModelProviderFactory, SavedStateRegistryOwner,
ActivityResultCaller {
// ...
LifecycleRegistry mLifecycleRegistry;
// ...
/**
* {@inheritDoc}
* <p>
* Overriding this method is no longer supported and this method will be made
* <code>final</code> in a future version of Fragment.
*/
@Override
@NonNull
public Lifecycle getLifecycle() {
return mLifecycleRegistry;
}
// ...
/**
* Constructor used by the default {@link FragmentFactory}. You must
* {@link FragmentManager#setFragmentFactory(FragmentFactory) set a custom FragmentFactory}
* if you want to use a non-default constructor to ensure that your constructor
* is called when the fragment is re-instantiated.
*
* <p>It is strongly recommended to supply arguments with {@link #setArguments}
* and later retrieved by the Fragment with {@link #getArguments}. These arguments
* are automatically saved and restored alongside the Fragment.
*
* <p>Applications should generally not implement a constructor. Prefer
* {@link #onAttach(Context)} instead. It is the first place application code can run where
* the fragment is ready to be used - the point where the fragment is actually associated with
* its context. Some applications may also want to implement {@link #onInflate} to retrieve
* attributes from a layout resource, although note this happens when the fragment is attached.
*/
public Fragment() {
initLifecycle();
}
// ...
private void initLifecycle() {
mLifecycleRegistry = new LifecycleRegistry(this);
mSavedStateRegistryController = SavedStateRegistryController.create(this);
// The default factory depends on the SavedStateRegistry so it
// needs to be reset when the SavedStateRegistry is reset
mDefaultFactory = null;
if (!mOnPreAttachedListeners.contains(mSavedStateAttachListener)) {
registerOnPreAttachListener(mSavedStateAttachListener);
}
}
// ...
}
요약
ComponentActivity
,Fragment
는LifecycleOwner
를 구현한다.(Lifecycle getLifecycle()
함수를 갖는다.)ComponentActivity
,Fragment
에서getLifecycle()
를 구현할 때LifecycleRegistry
라는Lifecycle
의 확장 클래스를 반환한다.
LifecycleRegistry 분석
LifecycleRegistry
는 추상 클래스인 Lifecycle
을 확장 및 구현하는 클래스다. 상태 관리를 하고 여러 옵저버를 다루는 것이 주요 책임이다.
// LifecycleRegistry.java
/**
* 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 {
// ...
}
상태 관리
private State mState
라는 변수가 현재의 상태를 나타낸다. 해당 값은 public method markState()
, setCurrentState()
, handleLifecycleEvent()
가 호출되면 변경된다.
// LifecycleRegistry.java
/**
* 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 {
// ...
private State mState;
// ...
/**
* 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) {
this(provider, true);
}
private LifecycleRegistry(@NonNull LifecycleOwner provider, boolean enforceMainThread) {
mLifecycleOwner = new WeakReference<>(provider);
mState = INITIALIZED;
mEnforceMainThread = enforceMainThread;
}
/**
* Moves the Lifecycle to the given state and dispatches necessary events to the observers.
*
* @param state new state
* @deprecated Use {@link #setCurrentState(State)}.
*/
@Deprecated
@MainThread
public void markState(@NonNull State state) {
enforceMainThreadIfNeeded("markState");
setCurrentState(state);
}
/**
* Moves the Lifecycle to the given state and dispatches necessary events to the observers.
*
* @param state new state
*/
@MainThread
public void setCurrentState(@NonNull State state) {
enforceMainThreadIfNeeded("setCurrentState");
moveToState(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) {
enforceMainThreadIfNeeded("handleLifecycleEvent");
moveToState(event.getTargetState());
}
private void moveToState(State next) {
if (mState == next) {
return;
}
mState = next;
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 FastSafeIterableMap<LifecycleObserver, ObserverWithState> mObserverMap
라는 맵으로 여러 옵저버를 관리하고 있다. 구체적으로는 addObserver()
의 인자로 받은 LifecycleObserver
를 ObserverWithState
(LifecycleRegistry
의 nested class)의 인스턴스로 매핑해주는 식이다.
// LifecycleRegistry.java
/**
* 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<>();
// ...
@Override
public void addObserver(@NonNull LifecycleObserver observer) {
enforceMainThreadIfNeeded("addObserver");
State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
if (previous != null) {
return;
}
LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
if (lifecycleOwner == null) {
// it is null we should be destroyed. Fallback quickly
return;
}
boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent;
State targetState = calculateTargetState(observer);
mAddingObserverCounter++;
while ((statefulObserver.mState.compareTo(targetState) < 0
&& mObserverMap.contains(observer))) {
pushParentState(statefulObserver.mState);
final Event event = Event.upFrom(statefulObserver.mState);
if (event == null) {
throw new IllegalStateException("no event up from " + statefulObserver.mState);
}
statefulObserver.dispatchEvent(lifecycleOwner, event);
popParentState();
// mState / subling may have been changed recalculate
targetState = calculateTargetState(observer);
}
if (!isReentrance) {
// we do sync only on the top level.
sync();
}
mAddingObserverCounter--;
}
// ...
}
ObserverWithState
는 State
, LifecycleEventObserver
, dispatchEvent()
를 가진다. 의문인 점은 생성자에서 LifecycleObserver
를 받는데 어떻게 LifecycleEventObserver
가 생기는지이다. 생성자에서 Lifecycling.lifecycleEventObserver(observer)
를 통해 변환이 수행된다.
// LifecycleRegistry.java
/**
* 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 {
// ...
static class ObserverWithState {
State mState;
LifecycleEventObserver mLifecycleObserver;
ObserverWithState(LifecycleObserver observer, State initialState) {
mLifecycleObserver = Lifecycling.lifecycleEventObserver(observer);
mState = initialState;
}
void dispatchEvent(LifecycleOwner owner, Event event) {
State newState = event.getTargetState();
mState = min(mState, newState);
mLifecycleObserver.onStateChanged(owner, event);
mState = newState;
}
}
}
Lifecycling
클래스에 LifecycleObserver
를 LifecycleEventObserver
로 변환하는 정적 함수가 있다.
// Lifecycling.java
/**
* Internal class to handle lifecycle conversion etc.
*
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public class Lifecycling {
// ...
@NonNull
static LifecycleEventObserver lifecycleEventObserver(Object object) {
boolean isLifecycleEventObserver = object instanceof LifecycleEventObserver;
boolean isFullLifecycleObserver = object instanceof FullLifecycleObserver;
if (isLifecycleEventObserver && isFullLifecycleObserver) {
return new FullLifecycleObserverAdapter((FullLifecycleObserver) object,
(LifecycleEventObserver) object);
}
if (isFullLifecycleObserver) {
return new FullLifecycleObserverAdapter((FullLifecycleObserver) object, null);
}
if (isLifecycleEventObserver) {
return (LifecycleEventObserver) object;
}
final Class<?> klass = object.getClass();
int type = getObserverConstructorType(klass);
if (type == GENERATED_CALLBACK) {
List<Constructor<? extends GeneratedAdapter>> constructors =
sClassToAdapters.get(klass);
if (constructors.size() == 1) {
GeneratedAdapter generatedAdapter = createGeneratedAdapter(
constructors.get(0), object);
return new SingleGeneratedAdapterObserver(generatedAdapter);
}
GeneratedAdapter[] adapters = new GeneratedAdapter[constructors.size()];
for (int i = 0; i < constructors.size(); i++) {
adapters[i] = createGeneratedAdapter(constructors.get(i), object);
}
return new CompositeGeneratedAdaptersObserver(adapters);
}
return new ReflectiveGenericLifecycleObserver(object);
}
// ...
}
// **LifecycleEventObserver.java**
/**
* Class that can receive any lifecycle change and dispatch it to the receiver.
* <p>
* If a class implements both this interface and
* {@link androidx.lifecycle.DefaultLifecycleObserver}, then
* methods of {@code DefaultLifecycleObserver} will be called first, and then followed by the call
* of {@link LifecycleEventObserver#onStateChanged(LifecycleOwner, Lifecycle.Event)}
* <p>
* If a class implements this interface and in the same time uses {@link OnLifecycleEvent}, then
* annotations will be ignored.
*/
public interface LifecycleEventObserver extends LifecycleObserver {
/**
* Called when a state transition event happens.
*
* @param source The source of the event
* @param event The event
*/
void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event);
}
다시 LifecycleRegistry
로 돌아와서, markState()
, setCurrentState()
, handleLifecycleEvent()
같은 퍼블릭 함수를 통해 상태가 변경되면, 등록된 옵저버들에 대해 dispatchEvent()
함수를 호출하게 된다.
// LifecycleRegistry.java
/**
* 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 {
// ...
/**
* Moves the Lifecycle to the given state and dispatches necessary events to the observers.
*
* @param state new state
* @deprecated Use {@link #setCurrentState(State)}.
*/
@Deprecated
@MainThread
public void markState(@NonNull State state) {
enforceMainThreadIfNeeded("markState");
setCurrentState(state);
}
/**
* Moves the Lifecycle to the given state and dispatches necessary events to the observers.
*
* @param state new state
*/
@MainThread
public void setCurrentState(@NonNull State state) {
enforceMainThreadIfNeeded("setCurrentState");
moveToState(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) {
enforceMainThreadIfNeeded("handleLifecycleEvent");
moveToState(event.getTargetState());
}
private void moveToState(State next) {
if (mState == next) {
return;
}
mState = next;
if (mHandlingEvent || mAddingObserverCounter != 0) {
mNewEventOccurred = true;
// we will figure out what to do on upper level.
return;
}
mHandlingEvent = true;
sync();
mHandlingEvent = false;
}
// ...
private void forwardPass(LifecycleOwner lifecycleOwner) {
Iterator<Map.Entry<LifecycleObserver, ObserverWithState>> ascendingIterator =
mObserverMap.iteratorWithAdditions();
while (ascendingIterator.hasNext() && !mNewEventOccurred) {
Map.Entry<LifecycleObserver, ObserverWithState> entry = ascendingIterator.next();
ObserverWithState observer = entry.getValue();
while ((observer.mState.compareTo(mState) < 0 && !mNewEventOccurred
&& mObserverMap.contains(entry.getKey()))) {
pushParentState(observer.mState);
final Event event = Event.upFrom(observer.mState);
if (event == null) {
throw new IllegalStateException("no event up from " + observer.mState);
}
observer.dispatchEvent(lifecycleOwner, event);
popParentState();
}
}
}
private void backwardPass(LifecycleOwner lifecycleOwner) {
Iterator<Map.Entry<LifecycleObserver, ObserverWithState>> descendingIterator =
mObserverMap.descendingIterator();
while (descendingIterator.hasNext() && !mNewEventOccurred) {
Map.Entry<LifecycleObserver, ObserverWithState> entry = descendingIterator.next();
ObserverWithState observer = entry.getValue();
while ((observer.mState.compareTo(mState) > 0 && !mNewEventOccurred
&& mObserverMap.contains(entry.getKey()))) {
Event event = Event.downFrom(observer.mState);
if (event == null) {
throw new IllegalStateException("no event down from " + observer.mState);
}
pushParentState(event.getTargetState());
observer.dispatchEvent(lifecycleOwner, 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() {
LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
if (lifecycleOwner == null) {
throw new IllegalStateException("LifecycleOwner of this LifecycleRegistry is already"
+ "garbage collected. It is too late to change lifecycle state.");
}
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);
}
Map.Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest();
if (!mNewEventOccurred && newest != null
&& mState.compareTo(newest.getValue().mState) > 0) {
forwardPass(lifecycleOwner);
}
}
mNewEventOccurred = false;
}
// ...
}
요약
LifecycleRegistry
는 여러 observers를 다룰 수 있는Lifecycle
의 확장 클래스이다.- 상태 관리
private State mState
라는 변수가 현재의 상태를 나타낸다. 해당 값은 public methodmarkState()
,setCurrentState()
,handleLifecycleEvent()
가 호출되면 변경된다.
- 옵저버 관리
mObserverMap
을 통해LifecycleObserver
를ObserverWithState
로 매핑하며 관리한다.ObserverWithState
는State
,LifecycleEventObserver
,dispatchEvent()
를 가진다.markState()
,setCurrentState()
,handleLifecycleEvent()
같은 퍼블릭 함수를 통해 상태가 변경되면, 등록된 옵저버들에 대해dispatchEvent()
함수를 호출하게 된다.
Lifecycle 분석
Lifecycle
은 Android Lifecycle을 가지는 객체를 정의한다. 내부에 정의된 public 메소드 및 클래스는 다음과 같다:
addObserver()
,removeObserver()
getCurrentState()
public enum class Event
: ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY, ON_ANY(An Event constant that can be used to match all events.)public enum class State
: DESTROYED, INITIALIZED, CREATED, STARTED, RESUMED
// Lifecycle.java
/**
* Defines an object that has an Android Lifecycle. {@link androidx.fragment.app.Fragment Fragment}
* and {@link androidx.fragment.app.FragmentActivity FragmentActivity} classes implement
* {@link LifecycleOwner} interface which has the {@link LifecycleOwner#getLifecycle()
* getLifecycle} method to access the Lifecycle. You can also implement {@link LifecycleOwner}
* in your own classes.
* <p>
* {@link Event#ON_CREATE}, {@link Event#ON_START}, {@link Event#ON_RESUME} events in this class
* are dispatched <b>after</b> the {@link LifecycleOwner}'s related method returns.
* {@link Event#ON_PAUSE}, {@link Event#ON_STOP}, {@link Event#ON_DESTROY} events in this class
* are dispatched <b>before</b> the {@link LifecycleOwner}'s related method is called.
* For instance, {@link Event#ON_START} will be dispatched after
* {@link android.app.Activity#onStart onStart} returns, {@link Event#ON_STOP} will be dispatched
* before {@link android.app.Activity#onStop onStop} is called.
* This gives you certain guarantees on which state the owner is in.
* <p>
* To observe lifecycle events call {@link #addObserver(LifecycleObserver)} passing an object
* that implements either {@link DefaultLifecycleObserver} or {@link LifecycleEventObserver}.
*/
public abstract class Lifecycle {
/**
* Lifecycle coroutines extensions stashes the CoroutineScope into this field.
*
* @hide used by lifecycle-common-ktx
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@NonNull
AtomicReference<Object> mInternalScopeRef = new AtomicReference<>();
/**
* Adds a LifecycleObserver that will be notified when the LifecycleOwner changes
* state.
* <p>
* The given observer will be brought to the current state of the LifecycleOwner.
* For example, if the LifecycleOwner is in {@link State#STARTED} state, the given observer
* will receive {@link Event#ON_CREATE}, {@link Event#ON_START} events.
*
* @param observer The observer to notify.
*/
@MainThread
public abstract void addObserver(@NonNull LifecycleObserver observer);
/**
* Removes the given observer from the observers list.
* <p>
* If this method is called while a state change is being dispatched,
* <ul>
* <li>If the given observer has not yet received that event, it will not receive it.
* <li>If the given observer has more than 1 method that observes the currently dispatched
* event and at least one of them received the event, all of them will receive the event and
* the removal will happen afterwards.
* </ul>
*
* @param observer The observer to be removed.
*/
@MainThread
public abstract void removeObserver(@NonNull LifecycleObserver observer);
/**
* Returns the current state of the Lifecycle.
*
* @return The current state of the Lifecycle.
*/
@MainThread
@NonNull
public abstract State getCurrentState();
@SuppressWarnings("WeakerAccess")
public enum Event {
/**
* Constant for onCreate event of the {@link LifecycleOwner}.
*/
ON_CREATE,
/**
* Constant for onStart event of the {@link LifecycleOwner}.
*/
ON_START,
/**
* Constant for onResume event of the {@link LifecycleOwner}.
*/
ON_RESUME,
/**
* Constant for onPause event of the {@link LifecycleOwner}.
*/
ON_PAUSE,
/**
* Constant for onStop event of the {@link LifecycleOwner}.
*/
ON_STOP,
/**
* Constant for onDestroy event of the {@link LifecycleOwner}.
*/
ON_DESTROY,
/**
* An {@link Event Event} constant that can be used to match all events.
*/
ON_ANY;
/**
* Returns the {@link Lifecycle.Event} that will be reported by a {@link Lifecycle}
* leaving the specified {@link Lifecycle.State} to a lower state, or {@code null}
* if there is no valid event that can move down from the given state.
*
* @param state the higher state that the returned event will transition down from
* @return the event moving down the lifecycle phases from state
*/
@Nullable
public static Event downFrom(@NonNull State state) {
switch (state) {
case CREATED:
return ON_DESTROY;
case STARTED:
return ON_STOP;
case RESUMED:
return ON_PAUSE;
default:
return null;
}
}
/**
* Returns the {@link Lifecycle.Event} that will be reported by a {@link Lifecycle}
* entering the specified {@link Lifecycle.State} from a higher state, or {@code null}
* if there is no valid event that can move down to the given state.
*
* @param state the lower state that the returned event will transition down to
* @return the event moving down the lifecycle phases to state
*/
@Nullable
public static Event downTo(@NonNull State state) {
switch (state) {
case DESTROYED:
return ON_DESTROY;
case CREATED:
return ON_STOP;
case STARTED:
return ON_PAUSE;
default:
return null;
}
}
/**
* Returns the {@link Lifecycle.Event} that will be reported by a {@link Lifecycle}
* leaving the specified {@link Lifecycle.State} to a higher state, or {@code null}
* if there is no valid event that can move up from the given state.
*
* @param state the lower state that the returned event will transition up from
* @return the event moving up the lifecycle phases from state
*/
@Nullable
public static Event upFrom(@NonNull State state) {
switch (state) {
case INITIALIZED:
return ON_CREATE;
case CREATED:
return ON_START;
case STARTED:
return ON_RESUME;
default:
return null;
}
}
/**
* Returns the {@link Lifecycle.Event} that will be reported by a {@link Lifecycle}
* entering the specified {@link Lifecycle.State} from a lower state, or {@code null}
* if there is no valid event that can move up to the given state.
*
* @param state the higher state that the returned event will transition up to
* @return the event moving up the lifecycle phases to state
*/
@Nullable
public static Event upTo(@NonNull State state) {
switch (state) {
case CREATED:
return ON_CREATE;
case STARTED:
return ON_START;
case RESUMED:
return ON_RESUME;
default:
return null;
}
}
/**
* Returns the new {@link Lifecycle.State} of a {@link Lifecycle} that just reported
* this {@link Lifecycle.Event}.
*
* Throws {@link IllegalArgumentException} if called on {@link #ON_ANY}, as it is a special
* value used by {@link OnLifecycleEvent} and not a real lifecycle event.
*
* @return the state that will result from this event
*/
@NonNull
public State getTargetState() {
switch (this) {
case ON_CREATE:
case ON_STOP:
return State.CREATED;
case ON_START:
case ON_PAUSE:
return State.STARTED;
case ON_RESUME:
return State.RESUMED;
case ON_DESTROY:
return State.DESTROYED;
case ON_ANY:
break;
}
throw new IllegalArgumentException(this + " has no target state");
}
}
/**
* Lifecycle states. You can consider the states as the nodes in a graph and
* {@link Event}s as the edges between these nodes.
*/
@SuppressWarnings("WeakerAccess")
public enum State {
/**
* Destroyed state for a LifecycleOwner. After this event, this Lifecycle will not dispatch
* any more events. For instance, for an {@link android.app.Activity}, this state is reached
* <b>right before</b> Activity's {@link android.app.Activity#onDestroy() onDestroy} call.
*/
DESTROYED,
/**
* Initialized state for a LifecycleOwner. For an {@link android.app.Activity}, this is
* the state when it is constructed but has not received
* {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} yet.
*/
INITIALIZED,
/**
* Created state for a LifecycleOwner. For an {@link android.app.Activity}, this state
* is reached in two cases:
* <ul>
* <li>after {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} call;
* <li><b>right before</b> {@link android.app.Activity#onStop() onStop} call.
* </ul>
*/
CREATED,
/**
* Started state for a LifecycleOwner. For an {@link android.app.Activity}, this state
* is reached in two cases:
* <ul>
* <li>after {@link android.app.Activity#onStart() onStart} call;
* <li><b>right before</b> {@link android.app.Activity#onPause() onPause} call.
* </ul>
*/
STARTED,
/**
* Resumed state for a LifecycleOwner. For an {@link android.app.Activity}, this state
* is reached after {@link android.app.Activity#onResume() onResume} is called.
*/
RESUMED;
/**
* Compares if this State is greater or equal to the given {@code state}.
*
* @param state State to compare with
* @return true if this State is greater or equal to the given {@code state}
*/
public boolean isAtLeast(@NonNull State state) {
return compareTo(state) >= 0;
}
}
}
활용 사례: Lifecycle.repeatOnLifecycle
androidx.lifecycle
에서 repeatOnLifecycle()
을 제공한다. 아래와 같은 코드를 통해, STOPPED 상태가 되었을 때 StateFlow의 관찰을 중지할 수 있다.(같이 보면 좋은 글: https://readystory.tistory.com/207)
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
viewModel.photoSelectionMode.collectLatest {
// ...
}
}
launch {
viewModel.groupAlbum.collect {
// ...
}
}
}
}
repeatOnLifecycle()
이 어떻게 구현되었는지 살펴보자.(전체 코드: RepeatOnLifecycle.kt)
LifecycleEventObserver
의 anonymous object를 생성하고 Lifecycle
의 addObserver()
를 통해 등록한다. repeatOnLifecycle(Lifecycle.State.STARTED)
일 때, LifecycleEventObserver
는 다음 동작을 수행한다:
- Event가
ON_START
이면: 파라미터로 전달받은 코드 블럭을 수행한다. - Event가
ON_STOP
이면: job을 취소한다.
// RepeatOnLifecycle.kt
/**
* Runs the given [block] in a new coroutine when `this` [Lifecycle] is at least at [state] and
* suspends the execution until `this` [Lifecycle] is [Lifecycle.State.DESTROYED].
*
* The [block] will cancel and re-launch as the lifecycle moves in and out of the target state.
*
* ```
* class MyActivity : AppCompatActivity() {
* override fun onCreate(savedInstanceState: Bundle?) {
* /* ... */
* // Runs the block of code in a coroutine when the lifecycle is at least STARTED.
* // The coroutine will be cancelled when the ON_STOP event happens and will
* // restart executing if the lifecycle receives the ON_START event again.
* lifecycleScope.launch {
* lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
* uiStateFlow.collect { uiState ->
* updateUi(uiState)
* }
* }
* }
* }
* }
* ```
*
* The best practice is to call this function when the lifecycle is initialized. For
* example, `onCreate` in an Activity, or `onViewCreated` in a Fragment. Otherwise, multiple
* repeating coroutines doing the same could be created and be executed at the same time.
*
* Repeated invocations of `block` will run serially, that is they will always wait for the
* previous invocation to fully finish before re-starting execution as the state moves in and out
* of the required state.
*
* Warning: [Lifecycle.State.INITIALIZED] is not allowed in this API. Passing it as a
* parameter will throw an [IllegalArgumentException].
*
* @param state [Lifecycle.State] in which `block` runs in a new coroutine. That coroutine
* will cancel if the lifecycle falls below that state, and will restart if it's in that state
* again.
* @param block The block to run when the lifecycle is at least in [state] state.
*/
public suspend fun Lifecycle.repeatOnLifecycle(
state: Lifecycle.State,
block: suspend CoroutineScope.() -> Unit
) {
require(state !== Lifecycle.State.INITIALIZED) {
"repeatOnLifecycle cannot start work with the INITIALIZED lifecycle state."
}
if (currentState === Lifecycle.State.DESTROYED) {
return
}
// This scope is required to preserve context before we move to Dispatchers.Main
coroutineScope {
withContext(Dispatchers.Main.immediate) {
// Check the current state of the lifecycle as the previous check is not guaranteed
// to be done on the main thread.
if (currentState === Lifecycle.State.DESTROYED) return@withContext
// Instance of the running repeating coroutine
var launchedJob: Job? = null
// Registered observer
var observer: LifecycleEventObserver? = null
try {
// Suspend the coroutine until the lifecycle is destroyed or
// the coroutine is cancelled
suspendCancellableCoroutine<Unit> { cont ->
// Lifecycle observers that executes `block` when the lifecycle reaches certain state, and
// cancels when it falls below that state.
val startWorkEvent = Lifecycle.Event.upTo(state)
val cancelWorkEvent = Lifecycle.Event.downFrom(state)
val mutex = Mutex()
observer = LifecycleEventObserver { _, event ->
if (event == startWorkEvent) {
// Launch the repeating work preserving the calling context
launchedJob = this@coroutineScope.launch {
// Mutex makes invocations run serially,
// coroutineScope ensures all child coroutines finish
mutex.withLock {
coroutineScope {
block()
}
}
}
return@LifecycleEventObserver
}
if (event == cancelWorkEvent) {
launchedJob?.cancel()
launchedJob = null
}
if (event == Lifecycle.Event.ON_DESTROY) {
cont.resume(Unit)
}
}
this@repeatOnLifecycle.addObserver(observer as LifecycleEventObserver)
}
} finally {
launchedJob?.cancel()
observer?.let {
this@repeatOnLifecycle.removeObserver(it)
}
}
}
}
}
/**
* [LifecycleOwner]'s extension function for [Lifecycle.repeatOnLifecycle] to allow an easier
* call to the API from LifecycleOwners such as Activities and Fragments.
*
* ```
* class MyActivity : AppCompatActivity() {
* override fun onCreate(savedInstanceState: Bundle?) {
* /* ... */
* // Runs the block of code in a coroutine when the lifecycle is at least STARTED.
* // The coroutine will be cancelled when the ON_STOP event happens and will
* // restart executing if the lifecycle receives the ON_START event again.
* lifecycleScope.launch {
* repeatOnLifecycle(Lifecycle.State.STARTED) {
* uiStateFlow.collect { uiState ->
* updateUi(uiState)
* }
* }
* }
* }
* }
* ```
*
* @see Lifecycle.repeatOnLifecycle
*/
public suspend fun LifecycleOwner.repeatOnLifecycle(
state: Lifecycle.State,
block: suspend CoroutineScope.() -> Unit
): Unit = lifecycle.repeatOnLifecycle(state, block)
'Android' 카테고리의 다른 글
Android Document | Foreground services > Overview (2) | 2023.11.21 |
---|---|
Android Document | About Services (1) | 2023.11.20 |
[Kotlin] Coroutines 예외 처리 방법 비교 (0) | 2023.08.28 |
[Android] Cleaning gradle using KTS(Kotlin DSL), Version Catalogs(toml) (0) | 2023.03.08 |
Why is @get:Rule used instead of @Rule in Kotlin? (0) | 2023.02.23 |