Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save belinwu/ee58228748be90293af3f2666f305295 to your computer and use it in GitHub Desktop.
Save belinwu/ee58228748be90293af3f2666f305295 to your computer and use it in GitHub Desktop.
Code for LazyLifecycleManager
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