Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: change screenshots debouncing approach to throttling #214

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## Next

- chore: change screenshots debouncing approach to throttling ([#214](https://github.com/PostHog/posthog-flutter/pull/214))
- Added `throttleDelayMs` config and deprecated `debouncerDelayMs` config.

## 3.9.3 - 2024-11-26

- no user facing changes
Expand Down
5 changes: 4 additions & 1 deletion posthog-android/api/posthog-android.api
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,22 @@ public final class com/posthog/android/replay/PostHogSessionReplayConfig {
public fun <init> (ZZZLcom/posthog/android/replay/PostHogDrawableConverter;)V
public fun <init> (ZZZLcom/posthog/android/replay/PostHogDrawableConverter;Z)V
public fun <init> (ZZZLcom/posthog/android/replay/PostHogDrawableConverter;ZJ)V
public synthetic fun <init> (ZZZLcom/posthog/android/replay/PostHogDrawableConverter;ZJILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (ZZZLcom/posthog/android/replay/PostHogDrawableConverter;ZJJ)V
public synthetic fun <init> (ZZZLcom/posthog/android/replay/PostHogDrawableConverter;ZJJILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getCaptureLogcat ()Z
public final fun getDebouncerDelayMs ()J
public final fun getDrawableConverter ()Lcom/posthog/android/replay/PostHogDrawableConverter;
public final fun getMaskAllImages ()Z
public final fun getMaskAllTextInputs ()Z
public final fun getScreenshot ()Z
public final fun getThrottleDelayMs ()J
public final fun setCaptureLogcat (Z)V
public final fun setDebouncerDelayMs (J)V
public final fun setDrawableConverter (Lcom/posthog/android/replay/PostHogDrawableConverter;)V
public final fun setMaskAllImages (Z)V
public final fun setMaskAllTextInputs (Z)V
public final fun setScreenshot (Z)V
public final fun setThrottleDelayMs (J)V
}

public class com/posthog/android/replay/internal/LogLine {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public class PostHogReplayIntegration(
decorView.onNextDraw(
mainHandler,
config.dateProvider,
config.sessionReplayConfig.debouncerDelayMs,
config.sessionReplayConfig.throttleDelayMs,
) {
if (!isSessionReplayEnabled || !isNativeSdk) {
return@onNextDraw
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,22 @@ public class PostHogSessionReplayConfig
* Defaults to 1000ms = 1s
* Ps: it was 500ms by default until version 3.8.2
*/
@PostHogExperimental
@Deprecated("Use throttleDelayMs instead")
public var debouncerDelayMs: Long = 1000,
)
/**
* Throttling delay used to reduce the number of snapshots captured and reduce performance impact
* This is used for capturing the view as a wireframe or screenshot
* The lower the number more snapshots will be captured but higher the performance impact
* Defaults to 1000ms = 1s
*/
@PostHogExperimental
public var throttleDelayMs: Long = 1000,
) {
init {
// for keeping back compatibility
@Suppress("DEPRECATION")
if (debouncerDelayMs != 1000L) {
throttleDelayMs = debouncerDelayMs
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ internal class NextDrawListener(
private val view: View,
mainHandler: MainHandler,
dateProvider: PostHogDateProvider,
debouncerDelayMs: Long,
throttleDelayMs: Long,
private val onDrawCallback: () -> Unit,
) : ViewTreeObserver.OnDrawListener {
private val debounce = Debouncer(mainHandler, dateProvider, debouncerDelayMs)
private val debounce = Throttler(mainHandler, dateProvider, throttleDelayMs)

override fun onDraw() {
debounce.debounce {
Expand All @@ -33,10 +33,10 @@ internal class NextDrawListener(
internal fun View.onNextDraw(
mainHandler: MainHandler,
dateProvider: PostHogDateProvider,
debouncerDelayMs: Long,
throttleDelayMs: Long,
onDrawCallback: () -> Unit,
): NextDrawListener {
val nextDrawListener = NextDrawListener(this, mainHandler, dateProvider, debouncerDelayMs, onDrawCallback)
val nextDrawListener = NextDrawListener(this, mainHandler, dateProvider, throttleDelayMs, onDrawCallback)
nextDrawListener.safelyRegisterForNextDraw()
return nextDrawListener
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,36 @@ import com.posthog.internal.PostHogDateProvider
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean

internal class Debouncer(
internal class Throttler(
private val mainHandler: MainHandler,
private val dateProvider: PostHogDateProvider,
private val debouncerDelayMs: Long,
private val throttleDelayMs: Long,
) {
private var lastCall = 0L
private val delayNs = TimeUnit.MILLISECONDS.toNanos(debouncerDelayMs)
private val hasPendingRunnable = AtomicBoolean(false)
private val delayNs = TimeUnit.MILLISECONDS.toNanos(throttleDelayMs)
private val isThrottling = AtomicBoolean(false)

/**
* Debounces the given [runnable] by delaying its execution until [delayNs] has passed since the last call.
*/
internal fun debounce(runnable: Runnable) {
if (lastCall == 0L) {
lastCall = dateProvider.nanoTime()
}
val currentTime = dateProvider.nanoTime()

val timePassedSinceLastExecution = dateProvider.nanoTime() - lastCall
if (timePassedSinceLastExecution >= delayNs) {
if (!hasPendingRunnable.get()) {
execute(runnable)
}
// Check if enough time has passed since the last execution
val timeSinceLastExecution = currentTime - lastCall
if (timeSinceLastExecution >= delayNs) {
// Execute immediately if enough time has passed
execute(runnable)
} else {
if (!hasPendingRunnable.getAndSet(true)) {
// If already throttling, ignore additional calls
if (!isThrottling.getAndSet(true)) {
mainHandler.handler.postDelayed({
try {
execute(runnable)
} finally {
hasPendingRunnable.set(false)
isThrottling.set(false) // Reset throttling after delay
}
}, debouncerDelayMs)
}, throttleDelayMs)
}
}
}
Expand Down
Loading