Forked from vishalratna-microsoft/LazyLifecycleManager.kt
Created
June 23, 2022 11:03
-
-
Save belinwu/ee58228748be90293af3f2666f305295 to your computer and use it in GitHub Desktop.
Code for LazyLifecycleManager
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
import android.app.Activity | |
import android.util.Log | |
import android.view.ViewTreeObserver | |
import androidx.annotation.MainThread | |
import androidx.lifecycle.Lifecycle | |
import androidx.lifecycle.LifecycleOwner | |
import com.******************.Barrier | |
import com.******************.Closure | |
import com.*******************.Once | |
/** | |
* LazyLifecycleManager is small construct to ensure that we initialise not so necessary | |
* things required in onCreate()/onStart()/onResume() lazily. By default it watches on the number of draws | |
* on decor view or has an SLA of 4 seconds. | |
* While one execution is in flight, this construct prevents the subsequent calls to lazy methods. | |
* Implementor should implement [LazyLifecycleCallbacks] and override its methods. | |
* And [LazyLifecycleManager.activate] should be called in onResume(). | |
*/ | |
// Currently built for activities. Will do it for fragment once this is successful. | |
///////////////////////////////////////////////////////////// | |
// NOTE: Should always be a activity scoped singleton. ///// | |
/////////////////////////////////////////////////////////// | |
class LazyLifecycleManager @JvmOverloads constructor(private val drawsToWait: Int = 15, private val sla: Int = 4000) { | |
private val executeLazyCreateOnce = Once() | |
private var state: State = State.Rest // Init with resting state | |
/** | |
* When activate() is called, it installs a barrier at the point that guards the target code. | |
* This method is meant for lazy initialisations or calls that could be deferred. | |
* This is not meant for calls that are purely dependent on activity lifecycle transitions eg. | |
* registering broadcast receivers in onStart() and unregistering them in onStop(). These type | |
* of complimentary calls should NOT be added in lazy callbacks as we do not provide lazy versions of | |
* onPause() and onStop() and it is not needed too. It is always good to depend on android in these cases. | |
*/ | |
@MainThread | |
fun activate(owner: LifecycleOwner) { | |
if (owner !is LazyLifecycleCallbacks || owner !is Activity) { | |
throw ClassCastException( | |
"Not able to cast LifeCycleOwner to LazyLifecycleCallbacks. " + | |
"Please implement LazyLifecycleCallbacks interface before calling activate()" | |
) | |
} | |
// Returns, if the implementation does not support lazy lifecycle callbacks. | |
if (owner.supportsLazyLifecycleCallbacks().not()) { | |
return | |
} | |
if (state == State.Active) { | |
Log.d(TAG, "Execution is in flight already bailing out!") | |
return | |
} | |
// Move to next state. | |
state = state.next() | |
val onPreDrawListener: ViewTreeObserver.OnPreDrawListener = object : ViewTreeObserver.OnPreDrawListener { | |
val lazyBarrier = Barrier.with(getCondition(owner, drawsToWait)) | |
.withSLA(sla.toLong(), true) | |
.runOnBreak { | |
val copyReference = this | |
with(owner as LazyLifecycleCallbacks) { | |
watchedView.viewTreeObserver.removeOnPreDrawListener(copyReference) | |
// Before executing any code check if activity is visible. If not skip and we will execute later | |
if (isOwnerVisible(owner)) { | |
// Execute onLazyOnCreate() only once per instance. | |
executeLazyCreateOnce.run(object : Closure { | |
override fun invoke() { | |
onLazyCreate() | |
} | |
}) | |
onLazyStart() | |
onLazyResume() | |
} else { | |
Log.d(TAG, "Skipped executing the lazy callbacks as the screen was not visible. Will execute next time!") | |
} | |
} | |
// Move to the next state. | |
state = state.next().also { Log.d(TAG, "Execution finished. Moving back to the resting state") } | |
}.startSLA() | |
override fun onPreDraw(): Boolean { | |
lazyBarrier.strike() | |
return true | |
} | |
} | |
(owner as LazyLifecycleCallbacks).watchedView.viewTreeObserver.addOnPreDrawListener(onPreDrawListener) | |
} | |
private fun isOwnerVisible(owner: LifecycleOwner) = owner.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED) | |
private fun getCondition(owner: LifecycleOwner, drawsToWait: Int): Barrier.Condition { | |
return object : Barrier.Condition { | |
var drawCount = 0 | |
val finalDrawsToWait = drawsToWait | |
override fun evaluate(): Boolean { | |
return if (owner.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) { | |
if (drawCount == finalDrawsToWait) { | |
true | |
} else { | |
if (drawCount > finalDrawsToWait) { | |
return true | |
} | |
drawCount++ | |
false | |
} | |
} else false | |
} | |
} | |
} | |
companion object { | |
const val TAG = "LazyLifecycleCallbacks" | |
} | |
private enum class State { | |
Rest { | |
override fun next(): State = Active | |
}, | |
Active { | |
override fun next(): State = Rest | |
}; | |
abstract fun next(): State | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment