diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a37500b9a..4a4eb745e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ ### Fixes +- Don't start a redundant UI interaction transaction when a transaction is already bound to the Scope ([#5491](https://github.com/getsentry/sentry-java/issues/5491)) + - Previously, `SentryGestureListener` always started a UI transaction and only afterwards skipped binding it to the Scope when a manually-bound transaction already existed, leaving the new transaction to be dropped as an idle transaction without children. - Fix potential NPE within `Scope.endSession()` ([#5657](https://github.com/getsentry/sentry-java/pull/5657)) ### Performance diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java index 8caffedad9..61a32b675d 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java @@ -244,6 +244,21 @@ private void startTracing(final @NotNull UiElement target, final @NotNull Gestur } } + // if there's already a transaction bound to the Scope (e.g. started manually by the user), we + // skip starting a new UI transaction: it would never be bound to the Scope in applyScope, would + // gather no children, and would be dropped as an idle transaction without children + final @Nullable ITransaction[] boundTransaction = {null}; + scopes.configureScope(scope -> boundTransaction[0] = scope.getTransaction()); + if (boundTransaction[0] != null) { + options + .getLogger() + .log( + SentryLevel.DEBUG, + "Transaction won't be created for view with id: %s since there's already a transaction bound to the Scope.", + viewIdentifier); + return; + } + // we can only bind to the scope if there's no running transaction final String name = getActivityName(activity) + "." + viewIdentifier; final String op = UI_ACTION + "." + getGestureType(eventType); diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerTracingTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerTracingTest.kt index fe994f4a82..9d7606bfe4 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerTracingTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerTracingTest.kt @@ -160,6 +160,18 @@ class SentryGestureListenerTracingTest { sut.onSingleTapUp(fixture.event) } + @Test + fun `when a transaction is already bound to the Scope, does not start a new UI transaction`() { + val sut = fixture.getSut() + val boundTransaction = SentryTracer(TransactionContext("bound", "op"), fixture.scopes) + whenever(fixture.scope.transaction).thenReturn(boundTransaction) + + sut.onSingleTapUp(fixture.event) + + verify(fixture.scopes, never()).startTransaction(any(), any()) + assertEquals(false, boundTransaction.isFinished) + } + @Test fun `stopTracing remove transaction from scope`() { val sut = fixture.getSut()