Created
October 14, 2020 22:25
-
-
Save objcode/29f11163a365874bd62ba3697f399389 to your computer and use it in GitHub Desktop.
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
package com.example.myapplication | |
import android.annotation.SuppressLint | |
import androidx.annotation.MainThread | |
import androidx.annotation.NonNull | |
import androidx.annotation.Nullable | |
import androidx.arch.core.executor.ArchTaskExecutor | |
import androidx.arch.core.internal.SafeIterableMap | |
import androidx.lifecycle.Lifecycle | |
import androidx.lifecycle.Lifecycle.State.DESTROYED | |
import androidx.lifecycle.Lifecycle.State.STARTED | |
import androidx.lifecycle.LifecycleEventObserver | |
import androidx.lifecycle.LifecycleOwner | |
import androidx.lifecycle.Observer | |
private sealed class PendingData<out T> { | |
object NotSet : PendingData<Nothing>() | |
class Ref<T>(val value: T) : PendingData<T>() | |
} | |
abstract class LiveData<T> { | |
/* synthetic access */ val mDataLock = Any() | |
@SuppressLint("RestrictedApi") | |
private val mObservers: SafeIterableMap<Observer<in T>, ObserverWrapper> = SafeIterableMap() | |
// how many observers are in active state | |
var mActiveCount/* synthetic access */ = 0 | |
// to handle active/inactive reentry, we guard with this boolean | |
private var mChangingActiveState = false | |
@Volatile | |
private var mData: T | |
// when setData is called, we set the pending data and actual data swap happens on the main | |
// thread | |
/* synthetic access */@Volatile | |
private var mPendingDataRef: PendingData<T> = PendingData.NotSet | |
val mPendingData: T? | |
get() = when (val pendingData = mPendingDataRef) { | |
is PendingData.NotSet -> null | |
is PendingData.Ref -> pendingData.value | |
} | |
var version: Int | |
private set | |
private var mDispatchingValue = false | |
private var mDispatchInvalidated = false | |
private val mPostValueRunnable: Runnable = Runnable { | |
var newValue: T | |
synchronized(mDataLock) { | |
newValue = mPendingData ?: return@Runnable | |
mPendingDataRef = PendingData.NotSet | |
} | |
setValue(newValue as T) | |
} | |
/** | |
* Creates a LiveData initialized with the given `value`. | |
* | |
* @param value initial value | |
*/ | |
constructor(value: T) { | |
mData = value | |
version = START_VERSION + 1 | |
} | |
/* Edit: This constructor must be removed for null-safety */ | |
/** | |
* Creates a LiveData with no value assigned to it. | |
*/ | |
// constructor() { | |
// mData = PendingData.NotSet | |
// version = START_VERSION | |
// } | |
private fun considerNotify(observer: ObserverWrapper) { | |
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 >= version) { | |
return | |
} | |
observer.mLastVersion = version | |
observer.mObserver?.onChanged(mData) | |
} | |
@SuppressLint("RestrictedApi") | |
fun /* synthetic access */dispatchingValue(initiator: ObserverWrapper?) { | |
var initiator = initiator | |
if (mDispatchingValue) { | |
mDispatchInvalidated = true | |
return | |
} | |
mDispatchingValue = true | |
do { | |
mDispatchInvalidated = false | |
if (initiator != null) { | |
considerNotify(initiator) | |
initiator = null | |
} else { | |
val iterator: Iterator<Map.Entry<Observer<in T>, ObserverWrapper>> = | |
mObservers.iteratorWithAdditions() | |
while (iterator.hasNext()) { | |
considerNotify(iterator.next().value) | |
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. | |
* | |
* | |
* The observer will only receive events if the owner is in [Lifecycle.State.STARTED] | |
* or [Lifecycle.State.RESUMED] state (active). | |
* | |
* | |
* If the owner moves to the [Lifecycle.State.DESTROYED] state, the observer will | |
* automatically be removed. | |
* | |
* | |
* When data changes while the `owner` is not active, it will not receive any updates. | |
* If it becomes active again, it will receive the last available data automatically. | |
* | |
* | |
* 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. | |
* | |
* | |
* If the given owner is already in [Lifecycle.State.DESTROYED] state, LiveData | |
* ignores the call. | |
* | |
* | |
* 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 | |
* [IllegalArgumentException]. | |
* | |
* @param owner The LifecycleOwner which controls the observer | |
* @param observer The observer that will receive the events | |
*/ | |
@SuppressLint("RestrictedApi") | |
@MainThread | |
fun observe(owner: LifecycleOwner, observer: Observer<in T>) { | |
assertMainThread("observe") | |
if (owner.getLifecycle().getCurrentState() === DESTROYED) { | |
// ignore | |
return | |
} | |
val wrapper = LifecycleBoundObserver(owner, observer) | |
val existing: ObserverWrapper? = mObservers.putIfAbsent(observer, wrapper) | |
if (existing != null && !existing.isAttachedTo(owner)) { | |
throw 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 | |
* [LiveData.observe] 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 [.removeObserver] to stop | |
* observing this LiveData. | |
* While LiveData has one of such observers, it will be considered | |
* as active. | |
* | |
* | |
* If the observer was already added with an owner to this LiveData, LiveData throws an | |
* [IllegalArgumentException]. | |
* | |
* @param observer The observer that will receive the events | |
*/ | |
@SuppressLint("RestrictedApi") | |
@MainThread | |
fun observeForever(observer: Observer<in T>) { | |
assertMainThread("observeForever") | |
val wrapper = AlwaysActiveObserver(observer) | |
val existing: ObserverWrapper? = mObservers.putIfAbsent(observer, wrapper) | |
if (existing is LifecycleBoundObserver) { | |
throw 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. | |
*/ | |
@SuppressLint("RestrictedApi") | |
@MainThread | |
fun removeObserver(observer: Observer<in T>) { | |
assertMainThread("removeObserver") | |
val removed: ObserverWrapper = mObservers.remove(observer) ?: return | |
removed.detachObserver() | |
removed.activeStateChanged(false) | |
} | |
/** | |
* Removes all observers that are tied to the given [LifecycleOwner]. | |
* | |
* @param owner The `LifecycleOwner` scope for the observers to be removed. | |
*/ | |
@MainThread | |
fun removeObservers(owner: LifecycleOwner) { | |
assertMainThread("removeObservers") | |
for (entry: Map.Entry<Observer<in T>, ObserverWrapper> in mObservers) { | |
if (entry.value.isAttachedTo(owner)) { | |
removeObserver(entry.key) | |
} | |
} | |
} | |
/** | |
* 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". | |
* | |
* | |
* 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 | |
*/ | |
@SuppressLint("RestrictedApi") | |
protected fun postValue(value: T) { | |
var postTask: Boolean | |
synchronized(mDataLock) { | |
postTask = mPendingData === PendingData.NotSet | |
mPendingDataRef = PendingData.Ref(value) | |
} | |
if (!postTask) { | |
return | |
} | |
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable) | |
} | |
/** | |
* Sets the value. If there are active observers, the value will be dispatched to them. | |
* | |
* | |
* This method must be called from the main thread. If you need set a value from a background | |
* thread, you can use [.postValue] | |
* | |
* @param value The new value | |
*/ | |
@MainThread | |
protected fun setValue(value: T) { | |
assertMainThread("setValue") | |
version++ | |
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 | |
*/ | |
@get:Nullable | |
val value: T? | |
get() { | |
val data = mData | |
return if (data !== NOT_SET) { | |
data as T | |
} else null | |
} | |
/** | |
* Called when the number of active observers change from 0 to 1. | |
* | |
* | |
* This callback can be used to know that this LiveData is being used thus should be kept | |
* up to date. | |
*/ | |
protected fun onActive() {} | |
/** | |
* Called when the number of active observers change from 1 to 0. | |
* | |
* | |
* This does not mean that there are no observers left, there may still be observers but their | |
* lifecycle states aren't [Lifecycle.State.STARTED] or [Lifecycle.State.RESUMED] | |
* (like an Activity in the back stack). | |
* | |
* | |
* You can check if there are observers via [.hasObservers]. | |
*/ | |
protected fun onInactive() {} | |
/** | |
* Returns true if this LiveData has observers. | |
* | |
* @return true if this LiveData has observers | |
*/ | |
@SuppressLint("RestrictedApi") | |
fun hasObservers(): Boolean { | |
return mObservers.size() > 0 | |
} | |
/** | |
* Returns true if this LiveData has active observers. | |
* | |
* @return true if this LiveData has active observers | |
*/ | |
fun hasActiveObservers(): Boolean { | |
return mActiveCount > 0 | |
} | |
@MainThread | |
fun changeActiveCounter(change: Int) { | |
var previousActiveCount = mActiveCount | |
mActiveCount += change | |
if (mChangingActiveState) { | |
return | |
} | |
mChangingActiveState = true | |
try { | |
while (previousActiveCount != mActiveCount) { | |
val needToCallActive = previousActiveCount == 0 && mActiveCount > 0 | |
val needToCallInactive = previousActiveCount > 0 && mActiveCount == 0 | |
previousActiveCount = mActiveCount | |
if (needToCallActive) { | |
onActive() | |
} else if (needToCallInactive) { | |
onInactive() | |
} | |
} | |
} finally { | |
mChangingActiveState = false | |
} | |
} | |
internal inner class LifecycleBoundObserver( | |
@NonNull owner: LifecycleOwner, | |
observer: Observer<in T>? | |
) : ObserverWrapper(observer), LifecycleEventObserver { | |
@NonNull | |
val mOwner: LifecycleOwner = owner | |
override fun shouldBeActive(): Boolean { | |
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED) | |
} | |
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { | |
var currentState: Lifecycle.State = mOwner.getLifecycle().getCurrentState() | |
if (currentState === DESTROYED) { | |
if (mObserver != null) { | |
removeObserver(mObserver) | |
} | |
return | |
} | |
var prevState: Lifecycle.State? = null | |
while (prevState !== currentState) { | |
prevState = currentState | |
activeStateChanged(shouldBeActive()) | |
currentState = mOwner.getLifecycle().getCurrentState() | |
} | |
} | |
} | |
abstract inner class ObserverWrapper internal constructor(observer: Observer<in T>?) { | |
val mObserver: Observer<in T>? | |
var mActive = false | |
var mLastVersion = START_VERSION | |
abstract fun shouldBeActive(): Boolean | |
fun isAttachedTo(owner: LifecycleOwner?): Boolean { | |
return false | |
} | |
fun detachObserver() {} | |
fun activeStateChanged(newActive: Boolean) { | |
if (newActive == mActive) { | |
return | |
} | |
// immediately set active state, so we'd never dispatch anything to inactive | |
// owner | |
mActive = newActive | |
changeActiveCounter(if (mActive) 1 else -1) | |
if (mActive) { | |
dispatchingValue(this) | |
} | |
} | |
init { | |
mObserver = observer | |
} | |
} | |
private inner class AlwaysActiveObserver internal constructor(observer: Observer<in T>?) : | |
ObserverWrapper(observer) { | |
override fun shouldBeActive(): Boolean { | |
return true | |
} | |
} | |
companion object { | |
val START_VERSION = -1 | |
val NOT_SET = Any() | |
@SuppressLint("RestrictedApi") | |
fun assertMainThread(methodName: String) { | |
if (!ArchTaskExecutor.getInstance().isMainThread()) { | |
throw 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