From 07834366cc74b58e075db672f0ca003976d99714 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Thu, 25 Jun 2026 13:34:01 +0200 Subject: [PATCH 1/3] perf(android): Gate Compose class probes behind their features The Compose gesture and view hierarchy class lookups ran during init regardless of configuration. Gate the gesture locators behind enableUserInteractionBreadcrumbs/Tracing and the Compose view hierarchy exporter behind attachViewHierarchy, so the Compose class probes only run when a feature that consumes them is enabled. Repeated probes of the Compose class across both blocks are deduplicated by LoadClass's cache. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../core/AndroidOptionsInitializer.java | 16 ++++--- .../core/AndroidOptionsInitializerTest.kt | 45 +++++++++++++++++++ 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java index 5704cf7d7d4..5bc91e10bda 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java @@ -211,15 +211,18 @@ static void initializeIntegrationsAndProcessors( final @NotNull LazyEvaluator isAndroidXScrollViewAvailable = loadClass.isClassAvailableLazy("androidx.core.view.ScrollingView", options); - final boolean isComposeUpstreamAvailable = - loadClass.isClassAvailable(COMPOSE_CLASS_NAME, options); - if (options.getGestureTargetLocators().isEmpty()) { + // Gated behind the features that actually consume these so the Compose class lookups only run + // when user interaction tracking or view hierarchy capture is enabled. Repeated COMPOSE_CLASS + // probes across both blocks are deduplicated by LoadClass's cache. + final boolean isUserInteractionEnabled = + options.isEnableUserInteractionBreadcrumbs() || options.isEnableUserInteractionTracing(); + if (isUserInteractionEnabled && options.getGestureTargetLocators().isEmpty()) { final List gestureTargetLocators = new ArrayList<>(2); gestureTargetLocators.add(new AndroidViewGestureTargetLocator(isAndroidXScrollViewAvailable)); final boolean isComposeAvailable = - (isComposeUpstreamAvailable + (loadClass.isClassAvailable(COMPOSE_CLASS_NAME, options) && loadClass.isClassAvailable( SENTRY_COMPOSE_GESTURE_INTEGRATION_CLASS_NAME, options)); @@ -229,8 +232,9 @@ static void initializeIntegrationsAndProcessors( options.setGestureTargetLocators(gestureTargetLocators); } - if (options.getViewHierarchyExporters().isEmpty() - && isComposeUpstreamAvailable + if (options.isAttachViewHierarchy() + && options.getViewHierarchyExporters().isEmpty() + && loadClass.isClassAvailable(COMPOSE_CLASS_NAME, options) && loadClass.isClassAvailable( SENTRY_COMPOSE_VIEW_HIERARCHY_INTEGRATION_CLASS_NAME, options)) { diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt index f8724d286f8..65e0452cb9c 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt @@ -806,6 +806,51 @@ class AndroidOptionsInitializerTest { assertTrue { fixture.sentryOptions.gestureTargetLocators[1] is ComposeGestureTargetLocator } } + @Test + fun `does not set gesture target locators when user interaction tracking is disabled`() { + fixture.sentryOptions.isEnableUserInteractionBreadcrumbs = false + fixture.sentryOptions.isEnableUserInteractionTracing = false + + fixture.initSutWithClassLoader( + classesToLoad = + listOf( + AndroidOptionsInitializer.COMPOSE_CLASS_NAME, + AndroidOptionsInitializer.SENTRY_COMPOSE_GESTURE_INTEGRATION_CLASS_NAME, + ) + ) + + assertTrue { fixture.sentryOptions.gestureTargetLocators.isEmpty() } + } + + @Test + fun `does not set compose view hierarchy exporter when attachViewHierarchy is disabled`() { + // attachViewHierarchy defaults to false + fixture.initSutWithClassLoader( + classesToLoad = + listOf( + AndroidOptionsInitializer.COMPOSE_CLASS_NAME, + AndroidOptionsInitializer.SENTRY_COMPOSE_VIEW_HIERARCHY_INTEGRATION_CLASS_NAME, + ) + ) + + assertTrue { fixture.sentryOptions.viewHierarchyExporters.isEmpty() } + } + + @Test + fun `sets compose view hierarchy exporter when attachViewHierarchy is enabled and compose is available`() { + fixture.sentryOptions.isAttachViewHierarchy = true + + fixture.initSutWithClassLoader( + classesToLoad = + listOf( + AndroidOptionsInitializer.COMPOSE_CLASS_NAME, + AndroidOptionsInitializer.SENTRY_COMPOSE_VIEW_HIERARCHY_INTEGRATION_CLASS_NAME, + ) + ) + + assertTrue { fixture.sentryOptions.viewHierarchyExporters.size == 1 } + } + @Test fun `AndroidMemoryCollector is set to options`() { fixture.initSut() From bba32562e11c35670dde81f9ccc511d0e10be42a Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Thu, 25 Jun 2026 13:34:56 +0200 Subject: [PATCH 2/3] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index adc39a85198..df99ecc2c59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Probe class availability without initializing the class during SDK init ([#5635](https://github.com/getsentry/sentry-java/pull/5635)) - Cache reflective class lookups and avoid double-probing during SDK init ([#5636](https://github.com/getsentry/sentry-java/pull/5636)) +- Only probe for Compose classes during init when user interaction tracking or view hierarchy capture is enabled ([#5637](https://github.com/getsentry/sentry-java/pull/5637)) ## 8.45.0 From 0da46b76ccb61f2f0ef3d0c4abd6ecf3426864a8 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Thu, 25 Jun 2026 13:45:52 +0200 Subject: [PATCH 3/3] changelog: move init reflection entries to Performance --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df99ecc2c59..f1f677ac534 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -### Internal +### Performance - Probe class availability without initializing the class during SDK init ([#5635](https://github.com/getsentry/sentry-java/pull/5635)) - Cache reflective class lookups and avoid double-probing during SDK init ([#5636](https://github.com/getsentry/sentry-java/pull/5636))