fix(gestures): Replace GestureDetectorCompat with lightweight detector to fix ANR#5138
fix(gestures): Replace GestureDetectorCompat with lightweight detector to fix ANR#5138
Conversation
…stureDetector to fix ANR GestureDetectorCompat internally uses Handler.sendMessage/removeMessages which acquires a synchronized lock on the main thread MessageQueue, plus recordGestureClassification triggers IPC calls. This caused ANRs under load (SDK-CRASHES-JAVA-596, 175K+ occurrences). Replace with a minimal custom detector that only detects click, scroll, and fling without any Handler scheduling, MessageQueue contention, or IPC overhead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Semver Impact of This PR🟢 Patch (bug fixes) 📋 Changelog PreviewThis is how your changes will appear in the changelog. New Features ✨
Bug Fixes 🐛
🤖 This preview updates automatically when you update the PR. |
...droid-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureDetector.java
Show resolved
Hide resolved
Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| ee747ae | 358.21 ms | 389.41 ms | 31.20 ms |
| 22f4345 | 325.23 ms | 454.66 ms | 129.43 ms |
| fcec2f2 | 328.91 ms | 387.75 ms | 58.84 ms |
| a416a65 | 333.78 ms | 410.37 ms | 76.59 ms |
| d15471f | 286.65 ms | 314.68 ms | 28.03 ms |
| 951caf7 | 323.66 ms | 392.82 ms | 69.16 ms |
| 17a0955 | 372.53 ms | 446.70 ms | 74.17 ms |
| 9fbb112 | 404.51 ms | 475.65 ms | 71.14 ms |
| dba088c | 333.98 ms | 381.16 ms | 47.18 ms |
| 3d205d0 | 352.15 ms | 432.53 ms | 80.38 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| ee747ae | 1.58 MiB | 2.10 MiB | 530.95 KiB |
| 22f4345 | 1.58 MiB | 2.29 MiB | 719.83 KiB |
| fcec2f2 | 1.58 MiB | 2.12 MiB | 551.50 KiB |
| a416a65 | 1.58 MiB | 2.12 MiB | 555.26 KiB |
| d15471f | 1.58 MiB | 2.13 MiB | 559.54 KiB |
| 951caf7 | 1.58 MiB | 2.13 MiB | 558.77 KiB |
| 17a0955 | 1.58 MiB | 2.10 MiB | 533.20 KiB |
| 9fbb112 | 1.58 MiB | 2.11 MiB | 539.18 KiB |
| dba088c | 1.58 MiB | 2.13 MiB | 558.99 KiB |
| 3d205d0 | 1.58 MiB | 2.10 MiB | 532.97 KiB |
Previous results on branch: rz/fix/anr-gesture-detector
Startup times
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 9adfd0d | 382.98 ms | 473.75 ms | 90.77 ms |
| 3c64e61 | 338.52 ms | 430.68 ms | 92.16 ms |
| 99696ad | 313.46 ms | 354.00 ms | 40.54 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 9adfd0d | 1.58 MiB | 2.29 MiB | 723.82 KiB |
| 3c64e61 | 1.58 MiB | 2.29 MiB | 723.86 KiB |
| 99696ad | 1.58 MiB | 2.29 MiB | 723.83 KiB |
...droid-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureDetector.java
Show resolved
Hide resolved
…velocity data Matches GestureDetector behavior: if consecutive ACTION_DOWN events arrive without an intervening ACTION_UP/ACTION_CANCEL, stale motion data could bleed into fling detection. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
...ndroid-core/src/main/java/io/sentry/android/core/internal/gestures/SentryWindowCallback.java
Show resolved
Hide resolved
…check UserInteractionIntegration gated itself on GestureDetectorCompat being available via classloader check, but SentryGestureDetector only uses Android SDK classes. Remove the check so the integration works without androidx.core. Also remove the stale proguard -keep rule. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sentry Build Distribution
|
| listener.onScroll(currentDownEvent, event, scrollX, scrollY); | ||
| isInTapRegion = false; | ||
| lastX = x; | ||
| lastY = y; | ||
| } | ||
| break; | ||
| } | ||
|
|
||
| case MotionEvent.ACTION_UP: | ||
| if (isInTapRegion) { | ||
| listener.onSingleTapUp(event); |
There was a problem hiding this comment.
Bug: The SentryGestureDetector doesn't handle multi-touch events, causing it to incorrectly fire onSingleTapUp callbacks during gestures like pinch-to-zoom.
Severity: MEDIUM
Suggested Fix
In the onTouchEvent method, add cases for ACTION_POINTER_DOWN and ACTION_POINTER_UP. In these cases, cancel the tap detection by setting the isInTapRegion flag to false. This will prevent onSingleTapUp from being called when a multi-touch gesture is in progress.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location:
sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureDetector.java#L60-L96
Potential issue: The `SentryGestureDetector.onTouchEvent` method does not handle
multi-touch events like `ACTION_POINTER_DOWN` or `ACTION_POINTER_UP`. When a user
performs a multi-touch gesture (e.g., pinch-to-zoom), the `isInTapRegion` flag remains
`true` after the first pointer goes down. When any pointer is lifted, an `ACTION_UP`
event is processed while `isInTapRegion` is still true, incorrectly triggering an
`onSingleTapUp` callback. This results in spurious click breadcrumbs being generated for
multi-touch gestures, leading to inaccurate user interaction telemetry.
Summary
GestureDetectorCompatwith a custom lightweightSentryGestureDetectorinSentryWindowCallbackto fix ANR SDK-CRASHES-JAVA-596 (175K+ occurrences, 574 users)GestureDetectorCompatinternally usesHandler.sendMessage/removeMessageswhich acquires asynchronizedlock on the main thread'sMessageQueue, plusrecordGestureClassification()triggers IPC/binder calls — both cause contention under loadSentryGestureListeneruses), eliminating all Handler scheduling, MessageQueue lock contention, and IPC overheadTest plan
SentryGestureDetectorTestwith 7 test cases: tap, no-tap, scroll with deltas, fling, slow release, cancel cleanup, sequential gesture resetSentryWindowCallbackTest(4 tests) passesSentryGestureListenerTestsuite passes./gradlew :sentry-android-core:testDebugUnitTestpasses🤖 Generated with Claude Code