Skip to content

Commit 539ca63

Browse files
authored
[ANR] Defer some class availability checks (#4825)
* added lazy class available check to LoadClass lazy checking available classes in: * ActivityFramesTracker * AndroidViewGestureTargetLocator
1 parent 725987d commit 539ca63

File tree

10 files changed

+64
-34
lines changed

10 files changed

+64
-34
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
- [ANR] Removed AndroidTransactionProfiler lock ([#4817](https://github.com/getsentry/sentry-java/pull/4817))
88

9+
### Improvements
10+
11+
- [ANR] Defer some class availability checks ([#4825](https://github.com/getsentry/sentry-java/pull/4825))
12+
913
### Dependencies
1014

1115
- Bump Native SDK from v0.11.2 to v0.11.3 ([#4810](https://github.com/getsentry/sentry-java/pull/4810))

sentry-android-core/src/main/java/io/sentry/android/core/ActivityFramesTracker.java

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import io.sentry.protocol.MeasurementValue;
1111
import io.sentry.protocol.SentryId;
1212
import io.sentry.util.AutoClosableReentrantLock;
13+
import io.sentry.util.LazyEvaluator;
1314
import java.util.HashMap;
1415
import java.util.Map;
1516
import java.util.WeakHashMap;
@@ -30,7 +31,7 @@
3031
*/
3132
public final class ActivityFramesTracker {
3233

33-
private @Nullable FrameMetricsAggregator frameMetricsAggregator = null;
34+
private @NotNull LazyEvaluator<FrameMetricsAggregator> frameMetricsAggregator;
3435
private @NotNull final SentryAndroidOptions options;
3536

3637
private final @NotNull Map<SentryId, Map<String, @NotNull MeasurementValue>>
@@ -41,17 +42,18 @@ public final class ActivityFramesTracker {
4142
private final @NotNull MainLooperHandler handler;
4243
protected @NotNull AutoClosableReentrantLock lock = new AutoClosableReentrantLock();
4344

45+
private final @NotNull LazyEvaluator<Boolean> androidXAvailable;
46+
4447
public ActivityFramesTracker(
4548
final @NotNull io.sentry.util.LoadClass loadClass,
4649
final @NotNull SentryAndroidOptions options,
4750
final @NotNull MainLooperHandler handler) {
4851

49-
final boolean androidXAvailable =
50-
loadClass.isClassAvailable("androidx.core.app.FrameMetricsAggregator", options.getLogger());
52+
androidXAvailable =
53+
loadClass.isClassAvailableLazy(
54+
"androidx.core.app.FrameMetricsAggregator", options.getLogger());
55+
frameMetricsAggregator = new LazyEvaluator<>(() -> new FrameMetricsAggregator());
5156

52-
if (androidXAvailable) {
53-
frameMetricsAggregator = new FrameMetricsAggregator();
54-
}
5557
this.options = options;
5658
this.handler = handler;
5759
}
@@ -67,15 +69,15 @@ public ActivityFramesTracker(
6769
final @NotNull io.sentry.util.LoadClass loadClass,
6870
final @NotNull SentryAndroidOptions options,
6971
final @NotNull MainLooperHandler handler,
70-
final @Nullable FrameMetricsAggregator frameMetricsAggregator) {
72+
final @NotNull FrameMetricsAggregator frameMetricsAggregator) {
7173

7274
this(loadClass, options, handler);
73-
this.frameMetricsAggregator = frameMetricsAggregator;
75+
this.frameMetricsAggregator = new LazyEvaluator<>(() -> frameMetricsAggregator);
7476
}
7577

7678
@VisibleForTesting
7779
public boolean isFrameMetricsAggregatorAvailable() {
78-
return frameMetricsAggregator != null
80+
return androidXAvailable.getValue()
7981
&& options.isEnableFramesTracking()
8082
&& !options.isEnablePerformanceV2();
8183
}
@@ -87,7 +89,8 @@ public void addActivity(final @NotNull Activity activity) {
8789
return;
8890
}
8991

90-
runSafelyOnUiThread(() -> frameMetricsAggregator.add(activity), "FrameMetricsAggregator.add");
92+
runSafelyOnUiThread(
93+
() -> frameMetricsAggregator.getValue().add(activity), "FrameMetricsAggregator.add");
9194
snapshotFrameCountsAtStart(activity);
9295
}
9396
}
@@ -104,11 +107,11 @@ private void snapshotFrameCountsAtStart(final @NotNull Activity activity) {
104107
return null;
105108
}
106109

107-
if (frameMetricsAggregator == null) {
110+
if (!androidXAvailable.getValue()) {
108111
return null;
109112
}
110113

111-
final @Nullable SparseIntArray[] framesRates = frameMetricsAggregator.getMetrics();
114+
final @Nullable SparseIntArray[] framesRates = frameMetricsAggregator.getValue().getMetrics();
112115

113116
int totalFrames = 0;
114117
int slowFrames = 0;
@@ -153,7 +156,7 @@ public void setMetrics(final @NotNull Activity activity, final @NotNull SentryId
153156
// there was no
154157
// Observers, See
155158
// https://android.googlesource.com/platform/frameworks/base/+/140ff5ea8e2d99edc3fbe63a43239e459334c76b
156-
runSafelyOnUiThread(() -> frameMetricsAggregator.remove(activity), null);
159+
runSafelyOnUiThread(() -> frameMetricsAggregator.getValue().remove(activity), null);
157160

158161
final @Nullable FrameCounts frameCounts = diffFrameCountsAtEnd(activity);
159162

@@ -215,8 +218,9 @@ public void setMetrics(final @NotNull Activity activity, final @NotNull SentryId
215218
public void stop() {
216219
try (final @NotNull ISentryLifecycleToken ignored = lock.acquire()) {
217220
if (isFrameMetricsAggregatorAvailable()) {
218-
runSafelyOnUiThread(() -> frameMetricsAggregator.stop(), "FrameMetricsAggregator.stop");
219-
frameMetricsAggregator.reset();
221+
runSafelyOnUiThread(
222+
() -> frameMetricsAggregator.getValue().stop(), "FrameMetricsAggregator.stop");
223+
frameMetricsAggregator.getValue().reset();
220224
}
221225
activityMeasurements.clear();
222226
}

sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,8 @@ static void initializeIntegrationsAndProcessors(
200200
options.setVersionDetector(new DefaultVersionDetector(options));
201201
}
202202

203-
final boolean isAndroidXScrollViewAvailable =
204-
loadClass.isClassAvailable("androidx.core.view.ScrollingView", options);
203+
final @NotNull LazyEvaluator<Boolean> isAndroidXScrollViewAvailable =
204+
loadClass.isClassAvailableLazy("androidx.core.view.ScrollingView", options);
205205
final boolean isComposeUpstreamAvailable =
206206
loadClass.isClassAvailable(COMPOSE_CLASS_NAME, options);
207207

sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/AndroidViewGestureTargetLocator.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import io.sentry.android.core.internal.util.ClassUtil;
99
import io.sentry.internal.gestures.GestureTargetLocator;
1010
import io.sentry.internal.gestures.UiElement;
11+
import io.sentry.util.LazyEvaluator;
1112
import org.jetbrains.annotations.ApiStatus;
1213
import org.jetbrains.annotations.NotNull;
1314
import org.jetbrains.annotations.Nullable;
@@ -17,9 +18,10 @@ public final class AndroidViewGestureTargetLocator implements GestureTargetLocat
1718

1819
private static final String ORIGIN = "old_view_system";
1920

20-
private final boolean isAndroidXAvailable;
21+
private final @NotNull LazyEvaluator<Boolean> isAndroidXAvailable;
2122

22-
public AndroidViewGestureTargetLocator(final boolean isAndroidXAvailable) {
23+
public AndroidViewGestureTargetLocator(
24+
final @NotNull LazyEvaluator<Boolean> isAndroidXAvailable) {
2325
this.isAndroidXAvailable = isAndroidXAvailable;
2426
}
2527

@@ -33,7 +35,7 @@ public AndroidViewGestureTargetLocator(final boolean isAndroidXAvailable) {
3335
if (targetType == UiElement.Type.CLICKABLE && isViewTappable(view)) {
3436
return createUiElement(view);
3537
} else if (targetType == UiElement.Type.SCROLLABLE
36-
&& isViewScrollable(view, isAndroidXAvailable)) {
38+
&& isViewScrollable(view, isAndroidXAvailable.getValue())) {
3739
return createUiElement(view);
3840
}
3941
return null;

sentry-android-core/src/test/java/io/sentry/android/core/ActivityFramesTrackerTest.kt

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import android.util.SparseIntArray
55
import androidx.core.app.FrameMetricsAggregator
66
import androidx.test.ext.junit.runners.AndroidJUnit4
77
import io.sentry.ILogger
8+
import io.sentry.SentryOptions
89
import io.sentry.protocol.MeasurementValue
910
import io.sentry.protocol.SentryId
11+
import io.sentry.util.LazyEvaluator
1012
import kotlin.test.Test
1113
import kotlin.test.assertEquals
1214
import kotlin.test.assertFalse
@@ -34,12 +36,14 @@ class ActivityFramesTrackerTest {
3436
options.isEnablePerformanceV2 = false
3537
}
3638

37-
fun getSut(mockAggregator: Boolean = true): ActivityFramesTracker =
38-
if (mockAggregator) {
39-
ActivityFramesTracker(loadClass, options, handler, aggregator)
40-
} else {
41-
ActivityFramesTracker(loadClass, options, handler)
42-
}
39+
fun getSut(isAndroidxAvailable: Boolean = true): ActivityFramesTracker {
40+
whenever(loadClass.isClassAvailableLazy(any(), any<ILogger>()))
41+
.thenReturn(LazyEvaluator { isAndroidxAvailable })
42+
whenever(loadClass.isClassAvailableLazy(any(), any<SentryOptions>()))
43+
.thenReturn(LazyEvaluator { isAndroidxAvailable })
44+
45+
return ActivityFramesTracker(loadClass, options, handler, aggregator)
46+
}
4347
}
4448

4549
private val fixture = Fixture()
@@ -340,23 +344,20 @@ class ActivityFramesTrackerTest {
340344

341345
@Test
342346
fun `addActivity does not throw if no AndroidX`() {
343-
whenever(fixture.loadClass.isClassAvailable(any(), any<ILogger>())).thenReturn(false)
344347
val sut = fixture.getSut(false)
345348

346349
sut.addActivity(fixture.activity)
347350
}
348351

349352
@Test
350353
fun `setMetrics does not throw if no AndroidX`() {
351-
whenever(fixture.loadClass.isClassAvailable(any(), any<ILogger>())).thenReturn(false)
352354
val sut = fixture.getSut(false)
353355

354356
sut.setMetrics(fixture.activity, fixture.sentryId)
355357
}
356358

357359
@Test
358360
fun `addActivity and setMetrics combined do not throw if no AndroidX`() {
359-
whenever(fixture.loadClass.isClassAvailable(any(), any<ILogger>())).thenReturn(false)
360361
val sut = fixture.getSut(false)
361362

362363
sut.addActivity(fixture.activity)
@@ -373,7 +374,6 @@ class ActivityFramesTrackerTest {
373374

374375
@Test
375376
fun `stop does not throw if no AndroidX`() {
376-
whenever(fixture.loadClass.isClassAvailable(any(), any<ILogger>())).thenReturn(false)
377377
val sut = fixture.getSut(false)
378378

379379
sut.stop()
@@ -390,9 +390,13 @@ class ActivityFramesTrackerTest {
390390

391391
@Test
392392
fun `takeMetrics returns null if no AndroidX`() {
393-
whenever(fixture.loadClass.isClassAvailable(any(), any<ILogger>())).thenReturn(false)
394393
val sut = fixture.getSut(false)
395394

395+
whenever(fixture.aggregator.metrics).thenReturn(emptyArray(), getArray())
396+
397+
sut.addActivity(fixture.activity)
398+
sut.setMetrics(fixture.activity, fixture.sentryId)
399+
396400
assertNull(sut.takeMetrics(fixture.sentryId))
397401
}
398402

sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerClickTest.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import io.sentry.Scope.IWithPropagationContext
1717
import io.sentry.ScopeCallback
1818
import io.sentry.SentryLevel.INFO
1919
import io.sentry.android.core.SentryAndroidOptions
20+
import io.sentry.util.LazyEvaluator
2021
import kotlin.test.Test
2122
import kotlin.test.assertEquals
2223
import org.mockito.kotlin.any
@@ -38,7 +39,7 @@ class SentryGestureListenerClickTest {
3839
SentryAndroidOptions().apply {
3940
isEnableUserInteractionBreadcrumbs = true
4041
isEnableUserInteractionTracing = true
41-
gestureTargetLocators = listOf(AndroidViewGestureTargetLocator(true))
42+
gestureTargetLocators = listOf(AndroidViewGestureTargetLocator(LazyEvaluator { true }))
4243
dsn = "https://[email protected]/proj"
4344
}
4445
val scopes = mock<IScopes>()

sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerScrollTest.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import io.sentry.ScopeCallback
2020
import io.sentry.SentryLevel
2121
import io.sentry.SentryLevel.INFO
2222
import io.sentry.android.core.SentryAndroidOptions
23+
import io.sentry.util.LazyEvaluator
2324
import kotlin.test.Test
2425
import kotlin.test.assertEquals
2526
import org.mockito.kotlin.any
@@ -46,7 +47,7 @@ class SentryGestureListenerScrollTest {
4647
dsn = "https://[email protected]/proj"
4748
isEnableUserInteractionBreadcrumbs = true
4849
isEnableUserInteractionTracing = true
49-
gestureTargetLocators = listOf(AndroidViewGestureTargetLocator(true))
50+
gestureTargetLocators = listOf(AndroidViewGestureTargetLocator(LazyEvaluator { true }))
5051
}
5152
val scopes = mock<IScopes>()
5253
val scope = mock<IScope>()

sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerTracingTest.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import io.sentry.TransactionOptions
2323
import io.sentry.android.core.SentryAndroidOptions
2424
import io.sentry.protocol.SentryId
2525
import io.sentry.protocol.TransactionNameSource
26+
import io.sentry.util.LazyEvaluator
2627
import kotlin.test.Test
2728
import kotlin.test.assertEquals
2829
import kotlin.test.assertNotEquals
@@ -65,7 +66,8 @@ class SentryGestureListenerTracingTest {
6566
options.tracesSampleRate = tracesSampleRate
6667
options.isEnableUserInteractionTracing = isEnableUserInteractionTracing
6768
options.isEnableUserInteractionBreadcrumbs = true
68-
options.gestureTargetLocators = listOf(AndroidViewGestureTargetLocator(true))
69+
options.gestureTargetLocators =
70+
listOf(AndroidViewGestureTargetLocator(LazyEvaluator { true }))
6971
options.isEnableAutoTraceIdGeneration = isEnableAutoTraceIdGeneration
7072

7173
whenever(scopes.options).thenReturn(options)

sentry/api/sentry.api

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7086,6 +7086,8 @@ public class io/sentry/util/LoadClass {
70867086
public fun <init> ()V
70877087
public fun isClassAvailable (Ljava/lang/String;Lio/sentry/ILogger;)Z
70887088
public fun isClassAvailable (Ljava/lang/String;Lio/sentry/SentryOptions;)Z
7089+
public fun isClassAvailableLazy (Ljava/lang/String;Lio/sentry/ILogger;)Lio/sentry/util/LazyEvaluator;
7090+
public fun isClassAvailableLazy (Ljava/lang/String;Lio/sentry/SentryOptions;)Lio/sentry/util/LazyEvaluator;
70897091
public fun loadClass (Ljava/lang/String;Lio/sentry/ILogger;)Ljava/lang/Class;
70907092
}
70917093

sentry/src/main/java/io/sentry/util/LoadClass.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,14 @@ public boolean isClassAvailable(
4545
final @NotNull String clazz, final @Nullable SentryOptions options) {
4646
return isClassAvailable(clazz, options != null ? options.getLogger() : null);
4747
}
48+
49+
public LazyEvaluator<Boolean> isClassAvailableLazy(
50+
final @NotNull String clazz, final @Nullable ILogger logger) {
51+
return new LazyEvaluator<>(() -> isClassAvailable(clazz, logger));
52+
}
53+
54+
public LazyEvaluator<Boolean> isClassAvailableLazy(
55+
final @NotNull String clazz, final @Nullable SentryOptions options) {
56+
return new LazyEvaluator<>(() -> isClassAvailable(clazz, options));
57+
}
4858
}

0 commit comments

Comments
 (0)