Skip to content

Commit 81a3c32

Browse files
authored
File I/O on main thread (#2382)
1 parent 87598a5 commit 81a3c32

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+684
-136
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- Add time-to-initial-display span to Activity transactions ([#2369](https://github.com/getsentry/sentry-java/pull/2369))
88
- Start a session after init if AutoSessionTracking is enabled ([#2356](https://github.com/getsentry/sentry-java/pull/2356))
99
- Provide automatic breadcrumbs and transactions for click/scroll events for Compose ([#2390](https://github.com/getsentry/sentry-java/pull/2390))
10+
- Add `blocked_main_thread` and `call_stack` to File I/O spans to detect performance issues ([#2382](https://github.com/getsentry/sentry-java/pull/2382))
1011

1112
### Dependencies
1213

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import androidx.core.app.FrameMetricsAggregator;
66
import io.sentry.MeasurementUnit;
77
import io.sentry.SentryLevel;
8-
import io.sentry.android.core.internal.util.MainThreadChecker;
8+
import io.sentry.android.core.internal.util.AndroidMainThreadChecker;
99
import io.sentry.protocol.MeasurementValue;
1010
import io.sentry.protocol.SentryId;
1111
import java.util.HashMap;
@@ -208,7 +208,7 @@ public synchronized void stop() {
208208

209209
private void runSafelyOnUiThread(final Runnable runnable, final String tag) {
210210
try {
211-
if (MainThreadChecker.isMainThread()) {
211+
if (AndroidMainThreadChecker.getInstance().isMainThread()) {
212212
runnable.run();
213213
} else {
214214
handler.post(

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import io.sentry.android.core.cache.AndroidEnvelopeCache;
1515
import io.sentry.android.core.internal.gestures.AndroidViewGestureTargetLocator;
1616
import io.sentry.android.core.internal.modules.AssetsModulesLoader;
17+
import io.sentry.android.core.internal.util.AndroidMainThreadChecker;
1718
import io.sentry.android.core.internal.util.SentryFrameMetricsCollector;
1819
import io.sentry.android.fragment.FragmentLifecycleIntegration;
1920
import io.sentry.android.timber.SentryTimberIntegration;
@@ -154,6 +155,7 @@ static void initializeIntegrationsAndProcessors(
154155
}
155156
options.setGestureTargetLocators(gestureTargetLocators);
156157
}
158+
options.setMainThreadChecker(AndroidMainThreadChecker.getInstance());
157159
}
158160

159161
private static void installDefaultIntegrations(

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import io.sentry.Integration;
66
import io.sentry.SentryLevel;
77
import io.sentry.SentryOptions;
8-
import io.sentry.android.core.internal.util.MainThreadChecker;
8+
import io.sentry.android.core.internal.util.AndroidMainThreadChecker;
99
import io.sentry.util.Objects;
1010
import java.io.Closeable;
1111
import java.io.IOException;
@@ -56,7 +56,7 @@ public void register(final @NotNull IHub hub, final @NotNull SentryOptions optio
5656
try {
5757
Class.forName("androidx.lifecycle.DefaultLifecycleObserver");
5858
Class.forName("androidx.lifecycle.ProcessLifecycleOwner");
59-
if (MainThreadChecker.isMainThread()) {
59+
if (AndroidMainThreadChecker.getInstance().isMainThread()) {
6060
addObserver(hub);
6161
} else {
6262
// some versions of the androidx lifecycle-process require this to be executed on the main
@@ -115,7 +115,7 @@ private void removeObserver() {
115115
@Override
116116
public void close() throws IOException {
117117
if (watcher != null) {
118-
if (MainThreadChecker.isMainThread()) {
118+
if (AndroidMainThreadChecker.getInstance().isMainThread()) {
119119
removeObserver();
120120
} else {
121121
// some versions of the androidx lifecycle-process require this to be executed on the main

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@
2626
import io.sentry.SentryBaseEvent;
2727
import io.sentry.SentryEvent;
2828
import io.sentry.SentryLevel;
29+
import io.sentry.android.core.internal.util.AndroidMainThreadChecker;
2930
import io.sentry.android.core.internal.util.ConnectivityChecker;
3031
import io.sentry.android.core.internal.util.DeviceOrientations;
31-
import io.sentry.android.core.internal.util.MainThreadChecker;
3232
import io.sentry.android.core.internal.util.RootChecker;
3333
import io.sentry.protocol.App;
3434
import io.sentry.protocol.Device;
@@ -217,7 +217,7 @@ private void setThreads(final @NotNull SentryEvent event) {
217217
if (event.getThreads() != null) {
218218
for (SentryThread thread : event.getThreads()) {
219219
if (thread.isCurrent() == null) {
220-
thread.setCurrent(MainThreadChecker.isMainThread(thread));
220+
thread.setCurrent(AndroidMainThreadChecker.getInstance().isMainThread(thread));
221221
}
222222
}
223223
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package io.sentry.android.core.internal.util;
2+
3+
import android.os.Looper;
4+
import io.sentry.util.thread.IMainThreadChecker;
5+
import org.jetbrains.annotations.ApiStatus;
6+
7+
/** Class that checks if a given thread is the Android Main/UI thread */
8+
@ApiStatus.Internal
9+
public final class AndroidMainThreadChecker implements IMainThreadChecker {
10+
11+
private static final AndroidMainThreadChecker instance = new AndroidMainThreadChecker();
12+
13+
public static AndroidMainThreadChecker getInstance() {
14+
return instance;
15+
}
16+
17+
private AndroidMainThreadChecker() {}
18+
19+
@Override
20+
public boolean isMainThread(final long threadId) {
21+
return Looper.getMainLooper().getThread().getId() == threadId;
22+
}
23+
}

sentry-android-core/src/main/java/io/sentry/android/core/internal/util/MainThreadChecker.java

Lines changed: 0 additions & 47 deletions
This file was deleted.

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import io.sentry.MainEventProcessor
99
import io.sentry.SentryOptions
1010
import io.sentry.android.core.cache.AndroidEnvelopeCache
1111
import io.sentry.android.core.internal.modules.AssetsModulesLoader
12+
import io.sentry.android.core.internal.util.AndroidMainThreadChecker
1213
import io.sentry.android.fragment.FragmentLifecycleIntegration
1314
import io.sentry.android.timber.SentryTimberIntegration
1415
import org.junit.runner.RunWith
@@ -447,4 +448,11 @@ class AndroidOptionsInitializerTest {
447448

448449
assertTrue { fixture.sentryOptions.modulesLoader is AssetsModulesLoader }
449450
}
451+
452+
@Test
453+
fun `AndroidMainThreadChecker is set to options`() {
454+
fixture.initSut()
455+
456+
assertTrue { fixture.sentryOptions.mainThreadChecker is AndroidMainThreadChecker }
457+
}
450458
}
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,23 @@ import kotlin.test.assertFalse
88
import kotlin.test.assertTrue
99

1010
@RunWith(AndroidJUnit4::class)
11-
class MainThreadCheckerTest {
11+
class AndroidMainThreadCheckerTest {
1212

1313
@Test
1414
fun `When calling isMainThread from the same thread, it should return true`() {
15-
assertTrue(MainThreadChecker.isMainThread())
15+
assertTrue(AndroidMainThreadChecker.getInstance().isMainThread)
1616
}
1717

1818
@Test
1919
fun `When calling isMainThread with the current thread, it should return true`() {
2020
val thread = Thread.currentThread()
21-
assertTrue(MainThreadChecker.isMainThread(thread))
21+
assertTrue(AndroidMainThreadChecker.getInstance().isMainThread(thread))
2222
}
2323

2424
@Test
2525
fun `When calling isMainThread from a different thread, it should return false`() {
2626
val thread = Thread()
27-
assertFalse(MainThreadChecker.isMainThread(thread))
27+
assertFalse(AndroidMainThreadChecker.getInstance().isMainThread(thread))
2828
}
2929

3030
@Test
@@ -33,7 +33,7 @@ class MainThreadCheckerTest {
3333
val sentryThread = SentryThread().apply {
3434
id = thread.id
3535
}
36-
assertTrue(MainThreadChecker.isMainThread(sentryThread))
36+
assertTrue(AndroidMainThreadChecker.getInstance().isMainThread(sentryThread))
3737
}
3838

3939
@Test
@@ -42,6 +42,6 @@ class MainThreadCheckerTest {
4242
val sentryThread = SentryThread().apply {
4343
id = thread.id
4444
}
45-
assertFalse(MainThreadChecker.isMainThread(sentryThread))
45+
assertFalse(AndroidMainThreadChecker.getInstance().isMainThread(sentryThread))
4646
}
4747
}

sentry-samples/sentry-samples-android/proguard-rules.pro

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
# https://developer.android.com/studio/build/shrink-code#decode-stack-trace
1717
-keepattributes LineNumberTable,SourceFile
1818

19-
2019
# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
2120
-keepclasseswithmembernames,includedescriptorclasses class * {
2221
native <methods>;

0 commit comments

Comments
 (0)