From 34c4474e337b9fb131206ad56e6b821f83b8f879 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Mon, 22 Jun 2026 17:40:21 +0200 Subject: [PATCH 1/2] perf(android): Reduce allocations in gesture target traversal (JAVA-534) Replace the per-gesture LinkedList BFS queue with an ArrayDeque in ViewUtils.findTarget and ComposeGestureTargetLocator, and drop the intermediate list allocated when enqueuing Compose children. LinkedList allocates a node object per element on every tap and scroll start; ArrayDeque is array-backed and allocates almost nothing per element. Traversal behavior is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../sentry/android/core/internal/gestures/ViewUtils.java | 6 +++--- .../compose/gestures/ComposeGestureTargetLocator.kt | 9 ++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/ViewUtils.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/ViewUtils.java index 501a05a5007..426c0db7cee 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/ViewUtils.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/ViewUtils.java @@ -7,7 +7,7 @@ import io.sentry.android.core.SentryAndroidOptions; import io.sentry.internal.gestures.GestureTargetLocator; import io.sentry.internal.gestures.UiElement; -import java.util.LinkedList; +import java.util.ArrayDeque; import java.util.List; import java.util.Queue; import org.jetbrains.annotations.ApiStatus; @@ -62,11 +62,11 @@ private static boolean touchWithinBounds( final UiElement.Type targetType) { final List locators = options.getGestureTargetLocators(); - final Queue queue = new LinkedList<>(); + final Queue queue = new ArrayDeque<>(); queue.add(decorView); @Nullable UiElement target = null; - while (queue.size() > 0) { + while (!queue.isEmpty()) { final View view = queue.poll(); if (!touchWithinBounds(view, x, y)) { diff --git a/sentry-compose/src/androidMain/kotlin/io/sentry/compose/gestures/ComposeGestureTargetLocator.kt b/sentry-compose/src/androidMain/kotlin/io/sentry/compose/gestures/ComposeGestureTargetLocator.kt index 54deb774c53..47dda6eda9c 100644 --- a/sentry-compose/src/androidMain/kotlin/io/sentry/compose/gestures/ComposeGestureTargetLocator.kt +++ b/sentry-compose/src/androidMain/kotlin/io/sentry/compose/gestures/ComposeGestureTargetLocator.kt @@ -15,7 +15,7 @@ import io.sentry.compose.boundsInWindow import io.sentry.internal.gestures.GestureTargetLocator import io.sentry.internal.gestures.UiElement import io.sentry.util.AutoClosableReentrantLock -import java.util.LinkedList +import java.util.ArrayDeque import java.util.Queue @OptIn(InternalComposeUiApi::class) @@ -45,7 +45,7 @@ public class ComposeGestureTargetLocator(private val logger: ILogger) : GestureT val rootLayoutNode = root.root // Pair - val queue: Queue> = LinkedList() + val queue: Queue> = ArrayDeque() queue.add(Pair(rootLayoutNode, null)) // the final tag to return, only relevant for clicks @@ -92,7 +92,10 @@ public class ComposeGestureTargetLocator(private val logger: ILogger) : GestureT } } } - queue.addAll(node.zSortedChildren.asMutableList().map { Pair(it, tag) }) + val children = node.zSortedChildren.asMutableList() + for (index in children.indices) { + queue.add(Pair(children[index], tag)) + } } } From df9744a8fcd2734e5fbfd29b4215e0d519897103 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Mon, 22 Jun 2026 17:41:26 +0200 Subject: [PATCH 2/2] changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d66d755a7c..030680c0487 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - Release `MediaMuxer` when a replay segment has no encodable frames to avoid a resource leak ([#5583](https://github.com/getsentry/sentry-java/pull/5583)) +### Internal + +- Reduce allocations in gesture target traversal by using `ArrayDeque` instead of `LinkedList` ([#5594](https://github.com/getsentry/sentry-java/pull/5594)) + ## 8.44.1 ### Fixes