Created
May 7, 2023 15:39
-
-
Save cesclong/e909629897c2d76de88e6dbae9c19fc5 to your computer and use it in GitHub Desktop.
LiveEvent LiveData
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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 com.pplive.archcomponent.jetpack.livedata; | |
import android.annotation.SuppressLint; | |
import androidx.lifecycle.GenericLifecycleObserver; | |
import androidx.lifecycle.Lifecycle; | |
import androidx.lifecycle.LifecycleOwner; | |
import androidx.lifecycle.Observer; | |
import androidx.annotation.MainThread; | |
import androidx.annotation.NonNull; | |
import androidx.annotation.Nullable; | |
import java.util.Iterator; | |
import java.util.Map; | |
import static androidx.lifecycle.Lifecycle.State.CREATED; | |
import static androidx.lifecycle.Lifecycle.State.DESTROYED; | |
/** | |
* | |
*/ | |
public abstract class LiveEvent<T> { | |
private final Object mDataLock = new Object(); | |
static final int START_VERSION = -1; | |
private static final Object NOT_SET = new Object(); | |
private SafeIterableMap<Observer<T>, ObserverWrapper> mObservers = | |
new SafeIterableMap<>(); | |
// how many observers are in active state | |
private int mActiveCount = 0; | |
private volatile Object mData = NOT_SET; | |
// when setData is called, we set the pending data and actual data swap happens on the main | |
// thread | |
private volatile Object mPendingData = NOT_SET; | |
private int mVersion = START_VERSION; | |
private boolean mDispatchingValue; | |
@SuppressWarnings("FieldCanBeLocal") | |
private boolean mDispatchInvalidated; | |
private class PostValueTask implements Runnable { | |
private Object newValue; | |
public PostValueTask(@NonNull Object newValue) { | |
this.newValue = newValue; | |
} | |
@Override | |
public void run() { | |
setValue((T) newValue); | |
} | |
} | |
private void considerNotify(ObserverWrapper observer) { | |
if (!observer.mActive) { | |
return; | |
} | |
// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet. | |
// | |
// we still first check observer.active to keep it as the entrance for events. So even if | |
// the observer moved to an active state, if we've not received that event, we better not | |
// notify for a more predictable notification order. | |
if (!observer.shouldBeActive()) { | |
observer.activeStateChanged(false); | |
return; | |
} | |
if (observer.mLastVersion >= mVersion) { | |
return; | |
} | |
observer.mLastVersion = mVersion; | |
//noinspection unchecked | |
try { | |
observer.mObserver.onChanged((T) mData); | |
} catch (ClassCastException e) { | |
e.printStackTrace(); | |
} | |
} | |
private void dispatchingValue(@Nullable ObserverWrapper initiator) { | |
if (mDispatchingValue) { | |
mDispatchInvalidated = true; | |
return; | |
} | |
mDispatchingValue = true; | |
do { | |
mDispatchInvalidated = false; | |
if (initiator != null) { | |
considerNotify(initiator); | |
initiator = null; | |
} else { | |
for (Iterator<Map.Entry<Observer<T>, ObserverWrapper>> iterator = | |
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) { | |
considerNotify(iterator.next().getValue()); | |
if (mDispatchInvalidated) { | |
break; | |
} | |
} | |
} | |
} while (mDispatchInvalidated); | |
mDispatchingValue = false; | |
} | |
/** | |
* Adds the given observer to the observers list within the lifespan of the given | |
* owner. The events are dispatched on the main thread. If LiveData already has data | |
* set, it will be delivered to the observer. | |
* <p> | |
* The observer will only receive events if the owner is in {@link Lifecycle.State#STARTED} | |
* or {@link Lifecycle.State#RESUMED} state (active). | |
* <p> | |
* If the owner moves to the {@link Lifecycle.State#DESTROYED} state, the observer will | |
* automatically be removed. | |
* <p> | |
* When data changes while the {@code owner} is not active, it will not receive any updates. | |
* If it becomes active again, it will receive the last available data automatically. | |
* <p> | |
* LiveData keeps a strong reference to the observer and the owner as long as the | |
* given LifecycleOwner is not destroyed. When it is destroyed, LiveData removes references to | |
* the observer & the owner. | |
* <p> | |
* If the given owner is already in {@link Lifecycle.State#DESTROYED} state, LiveData | |
* ignores the call. | |
* <p> | |
* If the given owner, observer tuple is already in the list, the call is ignored. | |
* If the observer is already in the list with another owner, LiveData throws an | |
* {@link IllegalArgumentException}. | |
* | |
* @param owner The LifecycleOwner which controls the observer | |
* @param observer The observer that will receive the events | |
*/ | |
@MainThread | |
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) { | |
if (owner.getLifecycle().getCurrentState() == DESTROYED) { | |
// ignore | |
return; | |
} | |
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer); | |
wrapper.mLastVersion = getVersion(); | |
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper); | |
if (existing != null && !existing.isAttachedTo(owner)) { | |
throw new IllegalArgumentException("Cannot add the same observer" | |
+ " with different lifecycles"); | |
} | |
if (existing != null) { | |
return; | |
} | |
owner.getLifecycle().addObserver(wrapper); | |
} | |
@MainThread | |
public void observeSticky(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) { | |
if (owner.getLifecycle().getCurrentState() == DESTROYED) { | |
// ignore | |
return; | |
} | |
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer); | |
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper); | |
if (existing != null && !existing.isAttachedTo(owner)) { | |
throw new IllegalArgumentException("Cannot add the same observer" | |
+ " with different lifecycles"); | |
} | |
if (existing != null) { | |
return; | |
} | |
owner.getLifecycle().addObserver(wrapper); | |
} | |
/** | |
* Adds the given observer to the observers list. This call is similar to | |
* {@link (LifecycleOwner, Observer)} with a LifecycleOwner, which | |
* is always active. This means that the given observer will receive all events and will never | |
* be automatically removed. You should manually call {@link #removeObserver(Observer)} to stop | |
* observing this LiveData. | |
* While LiveData has one of such observers, it will be considered | |
* as active. | |
* <p> | |
* If the observer was already added with an owner to this LiveData, LiveData throws an | |
* {@link IllegalArgumentException}. | |
* | |
* @param observer The observer that will receive the events | |
*/ | |
@MainThread | |
public void observeForever(@NonNull Observer<T> observer) { | |
AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer); | |
wrapper.mLastVersion = getVersion(); | |
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper); | |
if (existing != null && existing instanceof LiveEvent.LifecycleBoundObserver) { | |
throw new IllegalArgumentException("Cannot add the same observer" | |
+ " with different lifecycles"); | |
} | |
if (existing != null) { | |
return; | |
} | |
wrapper.activeStateChanged(true); | |
} | |
@MainThread | |
public void observeStickyForever(@NonNull Observer<T> observer) { | |
AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer); | |
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper); | |
if (existing != null && existing instanceof LiveEvent.LifecycleBoundObserver) { | |
throw new IllegalArgumentException("Cannot add the same observer" | |
+ " with different lifecycles"); | |
} | |
if (existing != null) { | |
return; | |
} | |
wrapper.activeStateChanged(true); | |
} | |
/** | |
* Removes the given observer from the observers list. | |
* | |
* @param observer The Observer to receive events. | |
*/ | |
@MainThread | |
public void removeObserver(@NonNull final Observer<T> observer) { | |
assertMainThread("removeObserver"); | |
ObserverWrapper removed = mObservers.remove(observer); | |
if (removed == null) { | |
return; | |
} | |
removed.detachObserver(); | |
removed.activeStateChanged(false); | |
} | |
/** | |
* Removes all observers that are tied to the given {@link LifecycleOwner}. | |
* | |
* @param owner The {@code LifecycleOwner} scope for the observers to be removed. | |
*/ | |
@SuppressWarnings("WeakerAccess") | |
@MainThread | |
public void removeObservers(@NonNull final LifecycleOwner owner) { | |
assertMainThread("removeObservers"); | |
for (Map.Entry<Observer<T>, ObserverWrapper> entry : mObservers) { | |
if (entry.getValue().isAttachedTo(owner)) { | |
removeObserver(entry.getKey()); | |
} | |
} | |
} | |
/** | |
* Posts a task to a main thread to set the given value. So if you have a following code | |
* executed in the main thread: | |
* <pre class="prettyprint"> | |
* liveData.postValue("a"); | |
* liveData.setValue("b"); | |
* </pre> | |
* The value "b" would be set at first and later the main thread would override it with | |
* the value "a". | |
* <p> | |
* If you called this method multiple times before a main thread executed a posted task, only | |
* the last value would be dispatched. | |
* | |
* @param value The new value | |
*/ | |
public void postValue(T value) { | |
MainThreadManager.getInstance().postToMainThread(new PostValueTask(value)); | |
} | |
/** | |
* Sets the value. If there are active observers, the value will be dispatched to them. | |
* <p> | |
* This method must be called from the main thread. If you need set a value from a background | |
* thread, you can use {@link #postValue(Object)} | |
* | |
* @param value The new value | |
*/ | |
@MainThread | |
public void setValue(T value) { | |
assertMainThread("setValue"); | |
mVersion++; | |
mData = value; | |
dispatchingValue(null); | |
} | |
/** | |
* Returns the current value. | |
* Note that calling this method on a background thread does not guarantee that the latest | |
* value set will be received. | |
* | |
* @return the current value | |
*/ | |
@Nullable | |
public T getValue() { | |
Object data = mData; | |
if (data != NOT_SET) { | |
//noinspection unchecked | |
return (T) data; | |
} | |
return null; | |
} | |
int getVersion() { | |
return mVersion; | |
} | |
/** | |
* Called when the number of active observers change to 1 from 0. | |
* <p> | |
* This callback can be used to know that this LiveData is being used thus should be kept | |
* up to date. | |
*/ | |
protected void onActive() { | |
} | |
/** | |
* Called when the number of active observers change from 1 to 0. | |
* <p> | |
* This does not mean that there are no observers left, there may still be observers but their | |
* lifecycle states aren't {@link Lifecycle.State#STARTED} or {@link Lifecycle.State#RESUMED} | |
* (like an Activity in the back stack). | |
* <p> | |
* You can check if there are observers via {@link #hasObservers()}. | |
*/ | |
protected void onInactive() { | |
} | |
/** | |
* Returns true if this LiveData has observers. | |
* | |
* @return true if this LiveData has observers | |
*/ | |
@SuppressWarnings("WeakerAccess") | |
public boolean hasObservers() { | |
return mObservers.size() > 0; | |
} | |
/** | |
* Returns true if this LiveData has active observers. | |
* | |
* @return true if this LiveData has active observers | |
*/ | |
@SuppressWarnings("WeakerAccess") | |
public boolean hasActiveObservers() { | |
return mActiveCount > 0; | |
} | |
/** | |
* determine when the observer is active, means the observer can receive message | |
* the default value is CREATED, means if the observer's state is above create, | |
* for example, the onCreate() of activity is called | |
* you can change this value to CREATED/STARTED/RESUMED | |
* determine on witch state, you can receive message | |
* | |
* @return | |
*/ | |
protected Lifecycle.State observerActiveLevel() { | |
return CREATED; | |
} | |
@SuppressLint("RestrictedApi") | |
class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver { | |
@NonNull | |
final LifecycleOwner mOwner; | |
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<T> observer) { | |
super(observer); | |
mOwner = owner; | |
} | |
@Override | |
boolean shouldBeActive() { | |
return mOwner.getLifecycle().getCurrentState().isAtLeast(observerActiveLevel()); | |
} | |
@Override | |
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) { | |
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) { | |
removeObserver(mObserver); | |
return; | |
} | |
activeStateChanged(shouldBeActive()); | |
} | |
@Override | |
boolean isAttachedTo(LifecycleOwner owner) { | |
return mOwner == owner; | |
} | |
@Override | |
void detachObserver() { | |
mOwner.getLifecycle().removeObserver(this); | |
} | |
} | |
private abstract class ObserverWrapper { | |
final Observer<T> mObserver; | |
boolean mActive; | |
int mLastVersion = START_VERSION; | |
ObserverWrapper(Observer<T> observer) { | |
mObserver = observer; | |
} | |
abstract boolean shouldBeActive(); | |
boolean isAttachedTo(LifecycleOwner owner) { | |
return false; | |
} | |
void detachObserver() { | |
} | |
void activeStateChanged(boolean newActive) { | |
if (newActive == mActive) { | |
return; | |
} | |
// immediately set active state, so we'd never dispatch anything to inactive | |
// owner | |
mActive = newActive; | |
boolean wasInactive = LiveEvent.this.mActiveCount == 0; | |
LiveEvent.this.mActiveCount += mActive ? 1 : -1; | |
if (wasInactive && mActive) { | |
onActive(); | |
} | |
if (LiveEvent.this.mActiveCount == 0 && !mActive) { | |
onInactive(); | |
} | |
if (mActive) { | |
dispatchingValue(this); | |
} | |
} | |
} | |
private class AlwaysActiveObserver extends ObserverWrapper { | |
AlwaysActiveObserver(Observer<T> observer) { | |
super(observer); | |
} | |
@Override | |
boolean shouldBeActive() { | |
return true; | |
} | |
} | |
private static void assertMainThread(String methodName) { | |
if (!MainThreadManager.getInstance().isMainThread()) { | |
throw new IllegalStateException("Cannot invoke " + methodName + " on a background" | |
+ " thread"); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment