Throttle with wait
Summary: Throttle events whilst keeping track of latest Reviewed By: LukeDefeo Differential Revision: D39652348 fbshipit-source-id: 9c8fb5a1bb92872985f46a62d79c6594a37e8340
This commit is contained in:
committed by
Facebook GitHub Bot
parent
fa9ba6f2d0
commit
cae15276f5
@@ -12,45 +12,47 @@ import android.view.View
|
||||
import android.view.ViewTreeObserver
|
||||
import com.facebook.flipper.plugins.uidebugger.LogTag
|
||||
import com.facebook.flipper.plugins.uidebugger.core.Context
|
||||
import com.facebook.flipper.plugins.uidebugger.scheduler.throttleLatest
|
||||
import java.lang.ref.WeakReference
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
typealias DecorView = View
|
||||
|
||||
/** Responsible for subscribing to updates to the content view of an activity */
|
||||
class DecorViewObserver(val context: Context) : TreeObserver<DecorView>() {
|
||||
|
||||
val throttleTimeMs = 500
|
||||
private val throttleTimeMs = 500L
|
||||
|
||||
private var nodeRef: WeakReference<View>? = null
|
||||
private var listener: ViewTreeObserver.OnPreDrawListener? = null
|
||||
|
||||
override val type = "DecorView"
|
||||
|
||||
private val waitScope = CoroutineScope(Dispatchers.IO)
|
||||
private val mainScope = CoroutineScope(Dispatchers.Main)
|
||||
|
||||
override fun subscribe(node: Any) {
|
||||
node as View
|
||||
nodeRef = WeakReference(node)
|
||||
|
||||
Log.i(LogTag, "Subscribing to decor view changes")
|
||||
|
||||
// TODO: there's a problem with this. Some future changes may have been
|
||||
// ignored and not sent. Need to keep track of the last one, always and react
|
||||
// accordingly.
|
||||
listener =
|
||||
object : ViewTreeObserver.OnPreDrawListener {
|
||||
var lastSend = 0L
|
||||
override fun onPreDraw(): Boolean {
|
||||
if (System.currentTimeMillis() - lastSend > throttleTimeMs) {
|
||||
traverseAndSend(context, node)
|
||||
val throttleSend =
|
||||
throttleLatest<WeakReference<View>?>(throttleTimeMs, waitScope, mainScope) { weakView ->
|
||||
weakView?.get()?.let { view -> traverseAndSend(context, view) }
|
||||
}
|
||||
|
||||
lastSend = System.currentTimeMillis()
|
||||
}
|
||||
return true
|
||||
}
|
||||
listener =
|
||||
ViewTreeObserver.OnPreDrawListener {
|
||||
throttleSend(nodeRef)
|
||||
true
|
||||
}
|
||||
|
||||
node.viewTreeObserver.addOnPreDrawListener(listener)
|
||||
// sometimes we are too late to the party and we miss the first draw
|
||||
listener?.onPreDraw()
|
||||
|
||||
// It can be the case that the DecorView the current observer owns has already
|
||||
// drawn. In this case, manually trigger an update.
|
||||
throttleSend(nodeRef)
|
||||
}
|
||||
|
||||
override fun unsubscribe() {
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.flipper.plugins.uidebugger.scheduler
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Throttle the execution of an executable for the specified interval.
|
||||
*
|
||||
* How does it work?
|
||||
*
|
||||
* The function `throttleLatest` returns a proxy for the given executable. This proxy captures the
|
||||
* latest argument/param that was used on the last invocation. If the throttle job does not exist or
|
||||
* has already completed, then create a new one.
|
||||
*
|
||||
* The job will wait on the waiting scope for the given amount of specified ms. Once it finishes
|
||||
* waiting, then it will execute the given executable on the main scope with the latest captured
|
||||
* param.
|
||||
*/
|
||||
fun <T> throttleLatest(
|
||||
intervalMs: Long,
|
||||
waitScope: CoroutineScope,
|
||||
mainScope: CoroutineScope,
|
||||
executable: (T) -> Unit
|
||||
): (T) -> Unit {
|
||||
var throttleJob: Job? = null
|
||||
var latestParam: T
|
||||
return { param: T ->
|
||||
latestParam = param
|
||||
if (throttleJob == null || throttleJob?.isCompleted == true) {
|
||||
throttleJob =
|
||||
waitScope.launch {
|
||||
delay(intervalMs)
|
||||
mainScope.launch { executable(latestParam) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user