Skip to content

Commit 4349a42

Browse files
buenaflorclaude
andcommitted
feat(extend-app-start): Add public Sentry app start extension API
Static bridge delegating to options.getAppStartExtender(): extendAppStart(), finishAppStart(), getExtendedAppStartSpan(). No-op when the SDK is disabled or the platform provides no app start extender. Completes the extend app start feature (4/4). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent d996425 commit 4349a42

3 files changed

Lines changed: 84 additions & 0 deletions

File tree

sentry/api/sentry.api

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2769,7 +2769,9 @@ public final class io/sentry/Sentry {
27692769
public static fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext;
27702770
public static fun distribution ()Lio/sentry/IDistributionApi;
27712771
public static fun endSession ()V
2772+
public static fun extendAppStart ()V
27722773
public static fun feedback ()Lio/sentry/IFeedbackApi;
2774+
public static fun finishExtendedAppStart ()V
27732775
public static fun flush (J)V
27742776
public static fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes;
27752777
public static fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes;
@@ -2778,6 +2780,7 @@ public final class io/sentry/Sentry {
27782780
public static fun getCurrentHub ()Lio/sentry/IHub;
27792781
public static fun getCurrentScopes ()Lio/sentry/IScopes;
27802782
public static fun getCurrentScopes (Z)Lio/sentry/IScopes;
2783+
public static fun getExtendedAppStartSpan ()Lio/sentry/ISpan;
27812784
public static fun getGlobalScope ()Lio/sentry/IScope;
27822785
public static fun getLastEventId ()Lio/sentry/protocol/SentryId;
27832786
public static fun getSpan ()Lio/sentry/ISpan;

sentry/src/main/java/io/sentry/Sentry.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,6 +1277,38 @@ public static void reportFullyDisplayed() {
12771277
getCurrentScopes().reportFullyDisplayed();
12781278
}
12791279

1280+
/**
1281+
* Begins extending the app start so launch-time work done after {@code Application.onCreate}
1282+
* (e.g. loading remote config before the first screen) is included in the app start measurement.
1283+
*
1284+
* <p>Intended to be called from {@code Application.onCreate} right after {@code
1285+
* SentryAndroid.init}. Only effective on Android with standalone app start tracing enabled;
1286+
* otherwise it is a no-op. Also no-ops if the app start already finished, none is in progress, or
1287+
* it was already extended (first call wins). Call {@link #finishExtendedAppStart()} once the
1288+
* extra work is done; if it is never called, the app start transaction is finished by its
1289+
* deadline and no extended measurement is reported.
1290+
*/
1291+
public static void extendAppStart() {
1292+
getCurrentScopes().getOptions().getAppStartExtender().extendAppStart();
1293+
}
1294+
1295+
/**
1296+
* Finishes the app start extension started by {@link #extendAppStart()}, allowing the app start
1297+
* transaction to complete. No-ops if the app start was not extended or this was already called.
1298+
*/
1299+
public static void finishExtendedAppStart() {
1300+
getCurrentScopes().getOptions().getAppStartExtender().finishExtendedAppStart();
1301+
}
1302+
1303+
/**
1304+
* Returns the active extended app start span, to attach child spans for the launch-time work
1305+
* being measured, or a no-op span when no extension is active (e.g. {@link #extendAppStart()} was
1306+
* not called, the app start window already passed, or standalone app start tracing is disabled).
1307+
*/
1308+
public static @NotNull ISpan getExtendedAppStartSpan() {
1309+
return getCurrentScopes().getOptions().getAppStartExtender().getExtendedAppStartSpan();
1310+
}
1311+
12801312
/**
12811313
* Configuration options callback
12821314
*

sentry/src/test/java/io/sentry/SentryTest.kt

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1769,4 +1769,53 @@ class SentryTest {
17691769
val scopes = Sentry.getCurrentScopes()
17701770
assertFalse(scopes.isNoOp)
17711771
}
1772+
1773+
// region app start extension
1774+
1775+
private fun initWithExtender(extender: IAppStartExtender) {
1776+
initForTest {
1777+
it.dsn = dsn
1778+
it.appStartExtender = extender
1779+
}
1780+
}
1781+
1782+
@Test
1783+
fun `extendAppStart delegates to the app start extender`() {
1784+
val extender = mock<IAppStartExtender>()
1785+
initWithExtender(extender)
1786+
1787+
Sentry.extendAppStart()
1788+
1789+
verify(extender).extendAppStart()
1790+
}
1791+
1792+
@Test
1793+
fun `finishExtendedAppStart delegates to the app start extender`() {
1794+
val extender = mock<IAppStartExtender>()
1795+
initWithExtender(extender)
1796+
1797+
Sentry.finishExtendedAppStart()
1798+
1799+
verify(extender).finishExtendedAppStart()
1800+
}
1801+
1802+
@Test
1803+
fun `getExtendedAppStartSpan delegates to the app start extender`() {
1804+
val span = mock<ISpan>()
1805+
val extender = mock<IAppStartExtender>()
1806+
whenever(extender.extendedAppStartSpan).thenReturn(span)
1807+
initWithExtender(extender)
1808+
1809+
assertSame(span, Sentry.getExtendedAppStartSpan())
1810+
}
1811+
1812+
@Test
1813+
fun `app start extension api is a no-op when the SDK is disabled`() {
1814+
// beforeTest called Sentry.close(), so the current scopes are NoOp.
1815+
Sentry.extendAppStart()
1816+
Sentry.finishExtendedAppStart()
1817+
assertTrue(Sentry.getExtendedAppStartSpan().isNoOp)
1818+
}
1819+
1820+
// endregion
17721821
}

0 commit comments

Comments
 (0)