Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import android.app.Application;
import android.os.StrictMode;
import io.sentry.ISpan;
import io.sentry.Sentry;
import io.sentry.samples.android.sqlite.SampleDatabases;

Expand All @@ -19,6 +20,8 @@ public void onCreate() {
strictMode();
super.onCreate();

extendAppStartExample();

SampleDatabases.INSTANCE.warmUp(this);

// Example how to initialize the SDK manually which allows access to SentryOptions callbacks.
Expand All @@ -36,6 +39,33 @@ public void onCreate() {
// });
}

// Example of extending the app start: launch-time work done here (after the SDK auto-inits) is
// included in the app start measurement. Requires standalone app start tracing
// (io.sentry.standalone-app-start-tracing.enable in the manifest). The artificial delays stand in
// for real launch work, e.g. loading remote config or feature flags before the first screen.
private void extendAppStartExample() {
Sentry.extendAppStart();

final ISpan extendedSpan = Sentry.getExtendedAppStartSpan();
final ISpan configSpan = extendedSpan.startChild("remote_config", "Load remote config");
artificialDelay(200);
configSpan.finish();

final ISpan flagsSpan = extendedSpan.startChild("feature_flags", "Fetch feature flags");
artificialDelay(100);
flagsSpan.finish();

Sentry.finishExtendedAppStart();
}

private static void artificialDelay(final long millis) {
try {
Thread.sleep(millis);
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
}
}

private void strictMode() {
// https://developer.android.com/reference/android/os/StrictMode
// StrictMode is a developer tool which detects things you might be doing by accident and
Expand Down
3 changes: 3 additions & 0 deletions sentry/api/sentry.api
Original file line number Diff line number Diff line change
Expand Up @@ -2769,7 +2769,9 @@ public final class io/sentry/Sentry {
public static fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext;
public static fun distribution ()Lio/sentry/IDistributionApi;
public static fun endSession ()V
public static fun extendAppStart ()V
public static fun feedback ()Lio/sentry/IFeedbackApi;
public static fun finishExtendedAppStart ()V
public static fun flush (J)V
public static fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes;
public static fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes;
Expand All @@ -2778,6 +2780,7 @@ public final class io/sentry/Sentry {
public static fun getCurrentHub ()Lio/sentry/IHub;
public static fun getCurrentScopes ()Lio/sentry/IScopes;
public static fun getCurrentScopes (Z)Lio/sentry/IScopes;
public static fun getExtendedAppStartSpan ()Lio/sentry/ISpan;
public static fun getGlobalScope ()Lio/sentry/IScope;
public static fun getLastEventId ()Lio/sentry/protocol/SentryId;
public static fun getSpan ()Lio/sentry/ISpan;
Expand Down
32 changes: 32 additions & 0 deletions sentry/src/main/java/io/sentry/Sentry.java
Original file line number Diff line number Diff line change
Expand Up @@ -1277,6 +1277,38 @@ public static void reportFullyDisplayed() {
getCurrentScopes().reportFullyDisplayed();
}

/**
* Begins extending the app start so launch-time work done after {@code Application.onCreate}
* (e.g. loading remote config before the first screen) is included in the app start measurement.
*
* <p>Intended to be called from {@code Application.onCreate} right after {@code
* SentryAndroid.init}. Only effective on Android with standalone app start tracing enabled;
* otherwise it is a no-op. Also no-ops if the app start already finished, none is in progress, or
* it was already extended (first call wins). Call {@link #finishExtendedAppStart()} once the
* extra work is done; if it is never called, the app start transaction is finished by its
* deadline and no extended measurement is reported.
*/
public static void extendAppStart() {
getCurrentScopes().getOptions().getAppStartExtender().extendAppStart();
}

/**
* Finishes the app start extension started by {@link #extendAppStart()}, allowing the app start
* transaction to complete. No-ops if the app start was not extended or this was already called.
*/
public static void finishExtendedAppStart() {
getCurrentScopes().getOptions().getAppStartExtender().finishExtendedAppStart();
}

/**
* Returns the active extended app start span, to attach child spans for the launch-time work
* being measured, or a no-op span when no extension is active (e.g. {@link #extendAppStart()} was
* not called, the app start window already passed, or standalone app start tracing is disabled).
*/
public static @NotNull ISpan getExtendedAppStartSpan() {
return getCurrentScopes().getOptions().getAppStartExtender().getExtendedAppStartSpan();
}

/**
* Configuration options callback
*
Expand Down
49 changes: 49 additions & 0 deletions sentry/src/test/java/io/sentry/SentryTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1769,4 +1769,53 @@ class SentryTest {
val scopes = Sentry.getCurrentScopes()
assertFalse(scopes.isNoOp)
}

// region app start extension

private fun initWithExtender(extender: IAppStartExtender) {
initForTest {
it.dsn = dsn
it.appStartExtender = extender
}
}

@Test
fun `extendAppStart delegates to the app start extender`() {
val extender = mock<IAppStartExtender>()
initWithExtender(extender)

Sentry.extendAppStart()

verify(extender).extendAppStart()
}

@Test
fun `finishExtendedAppStart delegates to the app start extender`() {
val extender = mock<IAppStartExtender>()
initWithExtender(extender)

Sentry.finishExtendedAppStart()

verify(extender).finishExtendedAppStart()
}

@Test
fun `getExtendedAppStartSpan delegates to the app start extender`() {
val span = mock<ISpan>()
val extender = mock<IAppStartExtender>()
whenever(extender.extendedAppStartSpan).thenReturn(span)
initWithExtender(extender)

assertSame(span, Sentry.getExtendedAppStartSpan())
}

@Test
fun `app start extension api is a no-op when the SDK is disabled`() {
// beforeTest called Sentry.close(), so the current scopes are NoOp.
Sentry.extendAppStart()
Sentry.finishExtendedAppStart()
assertTrue(Sentry.getExtendedAppStartSpan().isNoOp)
}

// endregion
}
Loading