feat(extend-app-start): [4/4] Add public Sentry app start extension API and update example app#5628
Merged
Merged
Conversation
…s wiring Introduces the @ApiStatus.Internal IAppStartExtender contract (extendAppStart / finishAppStart / getExtendedAppStartSpan) and a NoOp default, wired into SentryOptions. This is the naming-stable core bridge for the app start extension API; it is inert (returns NoOpSpan / no-ops) until the Android implementation and public Sentry facade land later in the stack. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…ix test name typo Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Contributor
|
📲 Install BuildsAndroid
|
Contributor
Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| c5023f1 | 314.61 ms | 362.49 ms | 47.88 ms |
| 4589e4d8f8388a43e637cd9e83a3527623dd3bab | 294.21 ms | 364.89 ms | 70.68 ms |
| 3ce7cf9 | 348.98 ms | 428.10 ms | 79.12 ms |
| c08cfa0 | 317.36 ms | 373.48 ms | 56.12 ms |
| 097718d | 323.29 ms | 363.42 ms | 40.13 ms |
| 259d761 | 328.98 ms | 388.63 ms | 59.65 ms |
| d35892b | 313.39 ms | 367.63 ms | 54.25 ms |
| 13d4096 | 307.35 ms | 362.96 ms | 55.61 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| c5023f1 | 0 B | 0 B | 0 B |
| 4589e4d8f8388a43e637cd9e83a3527623dd3bab | 0 B | 0 B | 0 B |
| 3ce7cf9 | 0 B | 0 B | 0 B |
| c08cfa0 | 0 B | 0 B | 0 B |
| 097718d | 0 B | 0 B | 0 B |
| 259d761 | 0 B | 0 B | 0 B |
| d35892b | 0 B | 0 B | 0 B |
| 13d4096 | 0 B | 0 B | 0 B |
buenaflor
force-pushed
the
feat/app-start-extension-materialize
branch
from
June 25, 2026 09:59
5dfb0e1 to
0bd259a
Compare
buenaflor
force-pushed
the
feat/app-start-extension-api
branch
from
June 25, 2026 10:00
bd89a68 to
90684a2
Compare
This was referenced Jun 25, 2026
buenaflor
force-pushed
the
feat/app-start-extension-materialize
branch
from
June 25, 2026 11:07
0bd259a to
0b6ada0
Compare
buenaflor
force-pushed
the
feat/app-start-extension-api
branch
from
June 25, 2026 11:07
90684a2 to
51ca64a
Compare
buenaflor
force-pushed
the
feat/app-start-extension-materialize
branch
from
June 25, 2026 11:38
0b6ada0 to
9e6f0a0
Compare
buenaflor
force-pushed
the
feat/app-start-extension-api
branch
from
June 25, 2026 11:38
51ca64a to
4c3438c
Compare
buenaflor
force-pushed
the
feat/app-start-extension-materialize
branch
from
June 25, 2026 12:10
9e6f0a0 to
976c29a
Compare
buenaflor
force-pushed
the
feat/app-start-extension-api
branch
from
June 25, 2026 12:10
4c3438c to
93464ef
Compare
buenaflor
force-pushed
the
feat/app-start-extension-materialize
branch
from
June 25, 2026 12:17
976c29a to
ac7a577
Compare
buenaflor
force-pushed
the
feat/app-start-extension-api
branch
from
June 25, 2026 12:17
93464ef to
a4baf22
Compare
buenaflor
force-pushed
the
feat/app-start-extension-materialize
branch
from
June 25, 2026 12:18
ac7a577 to
c3f3627
Compare
buenaflor
force-pushed
the
feat/app-start-extension-api
branch
from
June 25, 2026 12:18
a4baf22 to
c559b65
Compare
buenaflor
force-pushed
the
feat/app-start-extension-materialize
branch
from
June 25, 2026 12:23
c3f3627 to
cbaef2f
Compare
buenaflor
force-pushed
the
feat/app-start-extension-api
branch
from
June 25, 2026 12:23
c559b65 to
50357a5
Compare
…pStart Mirrors sentry-cocoa's finishExtendedAppLaunch() and makes the API name explicit about finishing the *extended* app start. Renames IAppStartExtender.finishAppStart() and the NoOpAppStartExtender implementation. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…ndroid extender Replaces the AppStartMetrics IAppStartExtender implementation and the deferred ExtendedAppStartSpan with a focused, lock-guarded AppStartExtension that owns the eager App Start transaction and extended span. AppStartMetrics now only holds the component and exposes isAppStartWindowOpen(). Inert until 3/4 registers the listener. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…ead of static scope lookup Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
… instead of a callback The listener now returns the created transaction+span (or null to decline) rather than calling back into onExtended() while extendAppStart() holds the lock. This removes the re-entrant lock acquisition and the A->B->A round trip, collapsing two public methods into one linear flow. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…o clear Drop comments that restate code or annotate omissions (region markers, getter javadoc, the reset call-site comment, the ExtendedAppStart/isActive docs). Rename AppStartExtension.reset() to clear() to match its owner AppStartMetrics.clear(). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…ting it The extension logs only two warnings, both on rare guard paths in extendAppStart(). Inline Sentry.getCurrentScopes().getOptions().getLogger() at those sites instead of holding a logger field set at init, dropping the field, setLogger(), and the AndroidOptionsInitializer wiring. extendAppStart() runs post-init, so the lookup always yields the configured logger. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…tartExtension Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…ed test After getExtendedAppStartSpan switched to getFinishDate(), the "after the span finished" case exercises the same branch as the reentrancy test (finishDate set -> NoOp); the isFinished stub was inert. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…xtension-materialize
The test names describe the scenarios; the inline comments restated them. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Move these component accessors into the component-extraction PR (they were introduced later in the wiring PR). Keeps the full AppStartExtension surface and its unit tests together here; the wiring PR only consumes them. Inert on its own. Regenerated apiDump. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…xtension-materialize
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
It is written without a lock in onAppStartSpansSent() and read across threads in canExtendAppStart() (via extendAppStart()), with no happens-before edge. volatile guarantees visibility, matching the AtomicInteger/AtomicBoolean siblings in the same check. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…xtension-materialize
…transaction Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
… inactive Mirrors Sentry.getSpan(): the extender reports null when there is no active extended span instead of a NoOpSpan, so callers can tell there is no span running. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…dAppStartSpan when inactive Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…xtension-materialize
…dAppStartSpan is nullable Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Mirrors Sentry.getSpan(): returns null when no extension is active instead of a NoOpSpan, so callers can tell there is no span running. Sample null-checks the span. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
romtsn
approved these changes
Jul 3, 2026
Base automatically changed from
feat/app-start-extension-materialize
to
feat/extend-app-start
July 6, 2026 14:48
…/app-start-extension-api # Conflicts: # sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java # sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt # sentry-android-core/src/test/java/io/sentry/android/core/performance/AppStartMetricsTest.kt # sentry/src/main/java/io/sentry/SentryOptions.java # sentry/src/test/java/io/sentry/SentryOptionsTest.kt
buenaflor
added a commit
that referenced
this pull request
Jul 7, 2026
* collection: extend app start * changelog * feat(extend-app-start): [1/4] Add IAppStartExtender bridge (#5605) * feat(extend-app-start): Add IAppStartExtender bridge and SentryOptions wiring Introduces the @ApiStatus.Internal IAppStartExtender contract (extendAppStart / finishAppStart / getExtendedAppStartSpan) and a NoOp default, wired into SentryOptions. This is the naming-stable core bridge for the app start extension API; it is inert (returns NoOpSpan / no-ops) until the Android implementation and public Sentry facade land later in the stack. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * chore(extend-app-start): Annotate NoOpAppStartExtender internal and fix test name typo Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * refactor(extend-app-start): Rename finishAppStart to finishExtendedAppStart Mirrors sentry-cocoa's finishExtendedAppLaunch() and makes the API name explicit about finishing the *extended* app start. Renames IAppStartExtender.finishAppStart() and the NoOpAppStartExtender implementation. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * feat(extend-app-start): Return null from getExtendedAppStartSpan when inactive Mirrors Sentry.getSpan(): the extender reports null when there is no active extended span instead of a NoOpSpan, so callers can tell there is no span running. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * test(extend-app-start): Remove NoOpAppStartExtenderTest per PR review Co-Authored-By: Claude Opus 4.8 <[email protected]> * fix(extend-app-start): Guard setAppStartExtender against null Match the NoOp-fallback convention used by other SentryOptions setters: accept a @nullable argument and coalesce to NoOpAppStartExtender so the @NotNull field and getter can never be nulled out. Co-Authored-By: Claude Opus 4.8 <[email protected]> --------- Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]> * test(extend-app-start): Use setter method for appStartExtender The @nullable setter and @NotNull getter no longer form a Kotlin mutable property, so use setAppStartExtender(...) instead of the property-assignment syntax (matches setTransportFactory/setReplayController). Co-Authored-By: Claude Opus 4.8 <[email protected]> * feat(extend-app-start): [2/4] Extract AppStartExtension component (#5606) * feat(extend-app-start): Add IAppStartExtender bridge and SentryOptions wiring Introduces the @ApiStatus.Internal IAppStartExtender contract (extendAppStart / finishAppStart / getExtendedAppStartSpan) and a NoOp default, wired into SentryOptions. This is the naming-stable core bridge for the app start extension API; it is inert (returns NoOpSpan / no-ops) until the Android implementation and public Sentry facade land later in the stack. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * chore(extend-app-start): Annotate NoOpAppStartExtender internal and fix test name typo Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * refactor(extend-app-start): Rename finishAppStart to finishExtendedAppStart Mirrors sentry-cocoa's finishExtendedAppLaunch() and makes the API name explicit about finishing the *extended* app start. Renames IAppStartExtender.finishAppStart() and the NoOpAppStartExtender implementation. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * feat(extend-app-start): Extract AppStartExtension component for the Android extender Replaces the AppStartMetrics IAppStartExtender implementation and the deferred ExtendedAppStartSpan with a focused, lock-guarded AppStartExtension that owns the eager App Start transaction and extended span. AppStartMetrics now only holds the component and exposes isAppStartWindowOpen(). Inert until 3/4 registers the listener. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * refactor(extend-app-start): Inject logger into AppStartExtension instead of static scope lookup Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * refactor(extend-app-start): Return ExtendedAppStart from the listener instead of a callback The listener now returns the created transaction+span (or null to decline) rather than calling back into onExtended() while extendAppStart() holds the lock. This removes the re-entrant lock acquisition and the A->B->A round trip, collapsing two public methods into one linear flow. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * chore(extend-app-start): Remove redundant comments and rename reset to clear Drop comments that restate code or annotate omissions (region markers, getter javadoc, the reset call-site comment, the ExtendedAppStart/isActive docs). Rename AppStartExtension.reset() to clear() to match its owner AppStartMetrics.clear(). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * refactor(extend-app-start): Inline the logger lookup instead of injecting it The extension logs only two warnings, both on rare guard paths in extendAppStart(). Inline Sentry.getCurrentScopes().getOptions().getLogger() at those sites instead of holding a logger field set at init, dropping the field, setLogger(), and the AndroidOptionsInitializer wiring. extendAppStart() runs post-init, so the lookup always yields the configured logger. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * chore(extend-app-start): Drop the class and listener javadocs on AppStartExtension Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * chore(extend-app-start): Trim finishTransaction/getExtendedEndTime comments Drop finishTransaction's javadoc (trivial body; it described caller context and waitForChildren behavior configured elsewhere) and reduce getExtendedEndTime's javadoc to a single inline note on the only non-obvious branch (deadline suppression). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * refactor(extend-app-start): Rename AppStartExtension.finishAppStart to finishExtendedAppStart Implements the renamed IAppStartExtender.finishExtendedAppStart(). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * chore(extend-app-start): Drop the redundant foreground-check comment on extendAppStart Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * test(extend-app-start): Drop the no-value getAppStartExtension test and trim comments Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * test(extend-app-start): Drop the no-op finishExtendedAppStart test Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(extend-app-start): Read the extended span finish date, not its finished flag getExtendedEndTime() gated on span.isFinished(), but finishing the extended span completes the waitForChildren transaction and runs the event processor re-entrantly within finishExtendedAppStart(), before the span's finished flag is set. The processor then saw an unfinished span and dropped the app start measurement whenever the extension finished after the first frame. Read getFinishDate() (set before the finish callback) instead, which also keeps the extended end controllable in tests. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(extend-app-start): End the extended transaction at the extended span end When the extended span finished after the timestamp passed to finishTransaction() but before that call ran (e.g. a synchronous extension in a headless start, where finishTransaction runs later at main-thread idle), waitForChildren had nothing left to wait for and the transaction kept the earlier passed timestamp. The extended span then ended after the transaction, and the app start vital exceeded the transaction duration. Finish at max(endTimestamp, extended span finish date) so the span is contained and the duration matches the vital. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(extend-app-start): Guard extendAppStartListener with the lock The setter wrote the field without synchronization while extendAppStart() reads it under the lock, leaving no happens-before edge. Acquire the same lock in the setter, consistent with the rest of the class's mutable state. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * style(extend-app-start): Trim verbose finishTransaction comment Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(extend-app-start): Read finish date in getExtendedAppStartSpan Finishing the extended span runs the event processor re-entrantly (via the waitForChildren transaction) before the span's isFinished() flag is set, while the finish timestamp is already in place. Reading isFinished() could therefore hand out a span that is already finishing. Switch to getFinishDate() == null, matching getExtendedEndTime(). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * style(extend-app-start): Trim duplicated reentrancy comment in getExtendedAppStartSpan Replace the repeated reentrancy explanation with a cross-reference to getExtendedEndTime(), which holds the canonical version. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * style(extend-app-start): Keep trimmed reentrancy comment within line length Single-line form fits the 100-char limit so spotless leaves it unwrapped. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * style(extend-app-start): Reword getExtendedAppStartSpan reentrancy comment Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * refactor(extend-app-start): Rename isAppStartWindowOpen to canExtendAppStart The predicate has a single consumer — the extend gate in AppStartExtension — so name it for that intent. Also drop the self-evident max-end comment in finishTransaction. Regenerated apiDump. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * test(extend-app-start): Drop redundant getExtendedAppStartSpan finished test After getExtendedAppStartSpan switched to getFinishDate(), the "after the span finished" case exercises the same branch as the reentrancy test (finishDate set -> NoOp); the isFinished stub was inert. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * feat(extend-app-start): Add setData and isExtended to AppStartExtension Move these component accessors into the component-extraction PR (they were introduced later in the wiring PR). Keeps the full AppStartExtension surface and its unit tests together here; the wiring PR only consumes them. Inert on its own. Regenerated apiDump. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(extend-app-start): Make shouldSendStartMeasurements volatile It is written without a lock in onAppStartSpansSent() and read across threads in canExtendAppStart() (via extendAppStart()), with no happens-before edge. volatile guarantees visibility, matching the AtomicInteger/AtomicBoolean siblings in the same check. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * docs(extend-app-start): Explain why AppStartExtension holds span and transaction Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * feat(extend-app-start): Return null from getExtendedAppStartSpan when inactive Mirrors Sentry.getSpan(): the extender reports null when there is no active extended span instead of a NoOpSpan, so callers can tell there is no span running. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * feat(extend-app-start): Return null from AppStartExtension.getExtendedAppStartSpan when inactive Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * test(extend-app-start): Use setter method for appStartExtender The @nullable setter and @NotNull getter no longer form a Kotlin mutable property, so use setAppStartExtender(...) instead of the property-assignment syntax (matches setTransportFactory/setReplayController). Co-Authored-By: Claude Opus 4.8 <[email protected]> --------- Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]> * feat(extend-app-start): [3/4] Eagerly create the extended app start transaction (#5608) * feat(extend-app-start): Add IAppStartExtender bridge and SentryOptions wiring Introduces the @ApiStatus.Internal IAppStartExtender contract (extendAppStart / finishAppStart / getExtendedAppStartSpan) and a NoOp default, wired into SentryOptions. This is the naming-stable core bridge for the app start extension API; it is inert (returns NoOpSpan / no-ops) until the Android implementation and public Sentry facade land later in the stack. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * chore(extend-app-start): Annotate NoOpAppStartExtender internal and fix test name typo Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * refactor(extend-app-start): Rename finishAppStart to finishExtendedAppStart Mirrors sentry-cocoa's finishExtendedAppLaunch() and makes the API name explicit about finishing the *extended* app start. Renames IAppStartExtender.finishAppStart() and the NoOpAppStartExtender implementation. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * feat(extend-app-start): Extract AppStartExtension component for the Android extender Replaces the AppStartMetrics IAppStartExtender implementation and the deferred ExtendedAppStartSpan with a focused, lock-guarded AppStartExtension that owns the eager App Start transaction and extended span. AppStartMetrics now only holds the component and exposes isAppStartWindowOpen(). Inert until 3/4 registers the listener. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * refactor(extend-app-start): Inject logger into AppStartExtension instead of static scope lookup Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * refactor(extend-app-start): Return ExtendedAppStart from the listener instead of a callback The listener now returns the created transaction+span (or null to decline) rather than calling back into onExtended() while extendAppStart() holds the lock. This removes the re-entrant lock acquisition and the A->B->A round trip, collapsing two public methods into one linear flow. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * chore(extend-app-start): Remove redundant comments and rename reset to clear Drop comments that restate code or annotate omissions (region markers, getter javadoc, the reset call-site comment, the ExtendedAppStart/isActive docs). Rename AppStartExtension.reset() to clear() to match its owner AppStartMetrics.clear(). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * refactor(extend-app-start): Inline the logger lookup instead of injecting it The extension logs only two warnings, both on rare guard paths in extendAppStart(). Inline Sentry.getCurrentScopes().getOptions().getLogger() at those sites instead of holding a logger field set at init, dropping the field, setLogger(), and the AndroidOptionsInitializer wiring. extendAppStart() runs post-init, so the lookup always yields the configured logger. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * chore(extend-app-start): Drop the class and listener javadocs on AppStartExtension Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * chore(extend-app-start): Trim finishTransaction/getExtendedEndTime comments Drop finishTransaction's javadoc (trivial body; it described caller context and waitForChildren behavior configured elsewhere) and reduce getExtendedEndTime's javadoc to a single inline note on the only non-obvious branch (deadline suppression). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * refactor(extend-app-start): Rename AppStartExtension.finishAppStart to finishExtendedAppStart Implements the renamed IAppStartExtender.finishExtendedAppStart(). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * feat(extend-app-start): Eagerly create the extended App Start transaction (standalone-only) Registers an extend-listener on AppStartExtension that eagerly creates the standalone app.start transaction + extended child span in Application.onCreate, held open via waitForChildren until Sentry.finishAppStart() or the deadline. The first activity continues the eager trace into ui.load (attaching the screen so it stays a foreground app.start) instead of creating a second app.start; headless finishes the eager txn. The app start vital is max(natural, extended) so an early finish never shortens it, and is suppressed on deadline. Standalone-only. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * chore(extend-app-start): Drop the redundant foreground-check comment on extendAppStart Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * test(extend-app-start): Replace the no-op finish test with isExtended coverage Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * test(extend-app-start): Drop the no-value getAppStartExtension test and trim comments Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * chore(extend-app-start): Drop the redundant foreground-check comment on extendAppStart Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * test(extend-app-start): Drop the no-value getAppStartExtension test and trim comments Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * test(extend-app-start): Drop the no-op finishExtendedAppStart test Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * chore(extend-app-start): Trim and de-duplicate comments in the eager app start path Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * chore(extend-app-start): Clarify the extensionActive comment Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(extend-app-start): Read the extended span finish date, not its finished flag getExtendedEndTime() gated on span.isFinished(), but finishing the extended span completes the waitForChildren transaction and runs the event processor re-entrantly within finishExtendedAppStart(), before the span's finished flag is set. The processor then saw an unfinished span and dropped the app start measurement whenever the extension finished after the first frame. Read getFinishDate() (set before the finish callback) instead, which also keeps the extended end controllable in tests. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(extend-app-start): End the extended transaction at the extended span end When the extended span finished after the timestamp passed to finishTransaction() but before that call ran (e.g. a synchronous extension in a headless start, where finishTransaction runs later at main-thread idle), waitForChildren had nothing left to wait for and the transaction kept the earlier passed timestamp. The extended span then ended after the transaction, and the app start vital exceeded the transaction duration. Finish at max(endTimestamp, extended span finish date) so the span is contained and the duration matches the vital. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * ref(extend-app-start): Rename extended app start span op to app.start.extended Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(extend-app-start): Guard extendAppStartListener with the lock The setter wrote the field without synchronization while extendAppStart() reads it under the lock, leaving no happens-before edge. Acquire the same lock in the setter, consistent with the rest of the class's mutable state. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(extend-app-start): Fix headless app start end time and duplicate txn Two issues in onHeadlessAppStart for extended starts: - The extended branch finished the eager transaction but never persisted appStartEndTime, leaving the continuation window unbounded so a later activity would wrongly continue the stale trace. Persist it on all paths. - The branch only checked isActive(), so if the extension already finished (finishExtendedAppStart or the deadline) before the headless idle ran, a second, empty standalone app.start was created. Guard on shouldSendStartMeasurements to avoid the duplicate. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * style(extend-app-start): Trim low-value comments in the headless fix Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * style(extend-app-start): Drop redundant extend-listener comment Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * style(extend-app-start): Trim redundant comments in onActivityCreated Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * style(extend-app-start): Remove explanatory comments in app start paths Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(extend-app-start): Consume stored app start trace on the extension path The first activity continuing an extended app.start did not clear the stored trace headers (only the headless-follow path did), so a later activity saw them and wrongly reused the finished app start trace. Clear them on the extension path too. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(extend-app-start): Only the launch activity attaches the app start screen While the extension was open, every activity's onActivityCreated re-attached app.vitals.start.screen to the eager app.start, so a second activity opened before finishExtendedAppStart() overwrote the launch screen. Gate the attach on isAppStart so only the launch activity sets it. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * Format code * style(extend-app-start): Trim verbose finishTransaction comment Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * style(extend-app-start): Trim verbose comments in app start paths Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(extend-app-start): Read finish date in getExtendedAppStartSpan Finishing the extended span runs the event processor re-entrantly (via the waitForChildren transaction) before the span's isFinished() flag is set, while the finish timestamp is already in place. Reading isFinished() could therefore hand out a span that is already finishing. Switch to getFinishDate() == null, matching getExtendedEndTime(). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * style(extend-app-start): Trim duplicated reentrancy comment in getExtendedAppStartSpan Replace the repeated reentrancy explanation with a cross-reference to getExtendedEndTime(), which holds the canonical version. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * style(extend-app-start): Keep trimmed reentrancy comment within line length Single-line form fits the 100-char limit so spotless leaves it unwrapped. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * style(extend-app-start): Drop two line-narrating test comments Remove comments that restated the adjacent test code (the eager extendAppStart call and the second-activity open). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * style(extend-app-start): Reword getExtendedAppStartSpan reentrancy comment Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * refactor(extend-app-start): Rename isAppStartWindowOpen to canExtendAppStart The predicate has a single consumer — the extend gate in AppStartExtension — so name it for that intent. Also drop the self-evident max-end comment in finishTransaction. Regenerated apiDump. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * test(extend-app-start): Drop redundant getExtendedAppStartSpan finished test After getExtendedAppStartSpan switched to getFinishDate(), the "after the span finished" case exercises the same branch as the reentrancy test (finishDate set -> NoOp); the isFinished stub was inert. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * test(extend-app-start): Drop narrating comments from app start tests The test names describe the scenarios; the inline comments restated them. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * feat(extend-app-start): Add setData and isExtended to AppStartExtension Move these component accessors into the component-extraction PR (they were introduced later in the wiring PR). Keeps the full AppStartExtension surface and its unit tests together here; the wiring PR only consumes them. Inert on its own. Regenerated apiDump. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * refactor(extend-app-start): Name app start branching booleans by intent Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(extend-app-start): Make shouldSendStartMeasurements volatile It is written without a lock in onAppStartSpansSent() and read across threads in canExtendAppStart() (via extendAppStart()), with no happens-before edge. volatile guarantees visibility, matching the AtomicInteger/AtomicBoolean siblings in the same check. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * docs(extend-app-start): Explain why AppStartExtension holds span and transaction Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * feat(extend-app-start): Return null from getExtendedAppStartSpan when inactive Mirrors Sentry.getSpan(): the extender reports null when there is no active extended span instead of a NoOpSpan, so callers can tell there is no span running. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * feat(extend-app-start): Return null from AppStartExtension.getExtendedAppStartSpan when inactive Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * test(extend-app-start): Assert null extended span now that getExtendedAppStartSpan is nullable Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * docs(extend-app-start): Explain single-use app start sampling decision The get-then-clear of the app start sampling decision in onExtendAppStartRequested looks redundant without context. Document that the decision is pre-rolled on the previous run, forces the eager app.start transaction's sampling and profiler binding, and must be cleared so the first ui.load can't also claim it. Co-Authored-By: Claude <[email protected]> * fix(extend-app-start): Bound the trace continuation window after an eager extension finishes The eager extendAppStart path persists trace headers so a later ui.load can continue the app.start trace, but never recorded the app start end time - that was only set on the headless path. If the extended transaction finished (user finish or deadline) before any activity, the first ui.load treated the continuation window as unbounded and joined the completed app-start trace no matter how much later it started. Persist the transaction's finish date via the transaction finished callback, which covers every finish path (finishExtendedAppStart, first frame, deadline), so the existing continuation window check bounds the extend path the same way it bounds the headless one. Co-Authored-By: Claude <[email protected]> * fix(extend-app-start): Skip warm-start reclassification while the extension is active A first activity arriving more than a minute after launch resets the app start span to the activity create time and flips the type to warm. While an app start extension is active this corrupts the extended vital: the eager app.start transaction stays anchored at process start while the measurement subtracts the reset span start, and a cold launch gets reported under the warm key. An active extension is the user explicitly saying the launch is still in progress, so let it pin the launch: skip the reclassification while the extension is active and apply the heuristic as before once it has finished. Co-Authored-By: Claude <[email protected]> * test(extend-app-start): Remove NoOpAppStartExtenderTest per PR review Co-Authored-By: Claude Opus 4.8 <[email protected]> --------- Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]> Co-authored-by: Sentry Github Bot <[email protected]> * feat(extend-app-start): [4/4] Add public Sentry app start extension API and update example app (#5628) * feat(extend-app-start): Add IAppStartExtender bridge and SentryOptions wiring Introduces the @ApiStatus.Internal IAppStartExtender contract (extendAppStart / finishAppStart / getExtendedAppStartSpan) and a NoOp default, wired into SentryOptions. This is the naming-stable core bridge for the app start extension API; it is inert (returns NoOpSpan / no-ops) until the Android implementation and public Sentry facade land later in the stack. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * chore(extend-app-start): Annotate NoOpAppStartExtender internal and fix test name typo Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * refactor(extend-app-start): Rename finishAppStart to finishExtendedAppStart Mirrors sentry-cocoa's finishExtendedAppLaunch() and makes the API name explicit about finishing the *extended* app start. Renames IAppStartExtender.finishAppStart() and the NoOpAppStartExtender implementation. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * feat(extend-app-start): Extract AppStartExtension component for the Android extender Replaces the AppStartMetrics IAppStartExtender implementation and the deferred ExtendedAppStartSpan with a focused, lock-guarded AppStartExtension that owns the eager App Start transaction and extended span. AppStartMetrics now only holds the component and exposes isAppStartWindowOpen(). Inert until 3/4 registers the listener. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * refactor(extend-app-start): Inject logger into AppStartExtension instead of static scope lookup Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * refactor(extend-app-start): Return ExtendedAppStart from the listener instead of a callback The listener now returns the created transaction+span (or null to decline) rather than calling back into onExtended() while extendAppStart() holds the lock. This removes the re-entrant lock acquisition and the A->B->A round trip, collapsing two public methods into one linear flow. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * chore(extend-app-start): Remove redundant comments and rename reset to clear Drop comments that restate code or annotate omissions (region markers, getter javadoc, the reset call-site comment, the ExtendedAppStart/isActive docs). Rename AppStartExtension.reset() to clear() to match its owner AppStartMetrics.clear(). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * refactor(extend-app-start): Inline the logger lookup instead of injecting it The extension logs only two warnings, both on rare guard paths in extendAppStart(). Inline Sentry.getCurrentScopes().getOptions().getLogger() at those sites instead of holding a logger field set at init, dropping the field, setLogger(), and the AndroidOptionsInitializer wiring. extendAppStart() runs post-init, so the lookup always yields the configured logger. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * chore(extend-app-start): Drop the class and listener javadocs on AppStartExtension Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * chore(extend-app-start): Trim finishTransaction/getExtendedEndTime comments Drop finishTransaction's javadoc (trivial body; it described caller context and waitForChildren behavior configured elsewhere) and reduce getExtendedEndTime's javadoc to a single inline note on the only non-obvious branch (deadline suppression). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * refactor(extend-app-start): Rename AppStartExtension.finishAppStart to finishExtendedAppStart Implements the renamed IAppStartExtender.finishExtendedAppStart(). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * feat(extend-app-start): Eagerly create the extended App Start transaction (standalone-only) Registers an extend-listener on AppStartExtension that eagerly creates the standalone app.start transaction + extended child span in Application.onCreate, held open via waitForChildren until Sentry.finishAppStart() or the deadline. The first activity continues the eager trace into ui.load (attaching the screen so it stays a foreground app.start) instead of creating a second app.start; headless finishes the eager txn. The app start vital is max(natural, extended) so an early finish never shortens it, and is suppressed on deadline. Standalone-only. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * 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) <[email protected]> * chore(extend-app-start): Drop the redundant foreground-check comment on extendAppStart Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * test(extend-app-start): Replace the no-op finish test with isExtended coverage Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * test(extend-app-start): Drop the no-value getAppStartExtension test and trim comments Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * chore(extend-app-start): Drop the redundant foreground-check comment on extendAppStart Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * test(extend-app-start): Drop the no-value getAppStartExtension test and trim comments Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * test(extend-app-start): Drop the no-op finishExtendedAppStart test Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * chore(extend-app-start): Trim and de-duplicate comments in the eager app start path Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * chore(extend-app-start): Clarify the extensionActive comment Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * feat(extend-app-start): Show extending the app start in the Android sample Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(extend-app-start): Read the extended span finish date, not its finished flag getExtendedEndTime() gated on span.isFinished(), but finishing the extended span completes the waitForChildren transaction and runs the event processor re-entrantly within finishExtendedAppStart(), before the span's finished flag is set. The processor then saw an unfinished span and dropped the app start measurement whenever the extension finished after the first frame. Read getFinishDate() (set before the finish callback) instead, which also keeps the extended end controllable in tests. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(extend-app-start): End the extended transaction at the extended span end When the extended span finished after the timestamp passed to finishTransaction() but before that call ran (e.g. a synchronous extension in a headless start, where finishTransaction runs later at main-thread idle), waitForChildren had nothing left to wait for and the transaction kept the earlier passed timestamp. The extended span then ended after the transaction, and the app start vital exceeded the transaction duration. Finish at max(endTimestamp, extended span finish date) so the span is contained and the duration matches the vital. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * ref(extend-app-start): Rename extended app start span op to app.start.extended Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(extend-app-start): Guard extendAppStartListener with the lock The setter wrote the field without synchronization while extendAppStart() reads it under the lock, leaving no happens-before edge. Acquire the same lock in the setter, consistent with the rest of the class's mutable state. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(extend-app-start): Fix headless app start end time and duplicate txn Two issues in onHeadlessAppStart for extended starts: - The extended branch finished the eager transaction but never persisted appStartEndTime, leaving the continuation window unbounded so a later activity would wrongly continue the stale trace. Persist it on all paths. - The branch only checked isActive(), so if the extension already finished (finishExtendedAppStart or the deadline) before the headless idle ran, a second, empty standalone app.start was created. Guard on shouldSendStartMeasurements to avoid the duplicate. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * style(extend-app-start): Trim low-value comments in the headless fix Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * style(extend-app-start): Drop redundant extend-listener comment Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * style(extend-app-start): Trim redundant comments in onActivityCreated Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * style(extend-app-start): Remove explanatory comments in app start paths Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(extend-app-start): Consume stored app start trace on the extension path The first activity continuing an extended app.start did not clear the stored trace headers (only the headless-follow path did), so a later activity saw them and wrongly reused the finished app start trace. Clear them on the extension path too. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(extend-app-start): Only the launch activity attaches the app start screen While the extension was open, every activity's onActivityCreated re-attached app.vitals.start.screen to the eager app.start, so a second activity opened before finishExtendedAppStart() overwrote the launch screen. Gate the attach on isAppStart so only the launch activity sets it. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * Format code * Format code * style(extend-app-start): Trim verbose finishTransaction comment Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * style(extend-app-start): Trim verbose comments in app start paths Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(extend-app-start): Read finish date in getExtendedAppStartSpan Finishing the extended span runs the event processor re-entrantly (via the waitForChildren transaction) before the span's isFinished() flag is set, while the finish timestamp is already in place. Reading isFinished() could therefore hand out a span that is already finishing. Switch to getFinishDate() == null, matching getExtendedEndTime(). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * style(extend-app-start): Trim duplicated reentrancy comment in getExtendedAppStartSpan Replace the repeated reentrancy explanation with a cross-reference to getExtendedEndTime(), which holds the canonical version. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * style(extend-app-start): Keep trimmed reentrancy comment within line length Single-line form fits the 100-char limit so spotless leaves it unwrapped. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * style(extend-app-start): Drop two line-narrating test comments Remove comments that restated the adjacent test code (the eager extendAppStart call and the second-activity open). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * style(extend-app-start): Reword getExtendedAppStartSpan reentrancy comment Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * refactor(extend-app-start): Rename isAppStartWindowOpen to canExtendAppStart The predicate has a single consumer — the extend gate in AppStartExtension — so name it for that intent. Also drop the self-evident max-end comment in finishTransaction. Regenerated apiDump. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * test(extend-app-start): Drop redundant getExtendedAppStartSpan finished test After getExtendedAppStartSpan switched to getFinishDate(), the "after the span finished" case exercises the same branch as the reentrancy test (finishDate set -> NoOp); the isFinished stub was inert. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * test(extend-app-start): Drop narrating comments from app start tests The test names describe the scenarios; the inline comments restated them. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * feat(extend-app-start): Add setData and isExtended to AppStartExtension Move these component accessors into the component-extraction PR (they were introduced later in the wiring PR). Keeps the full AppStartExtension surface and its unit tests together here; the wiring PR only consumes them. Inert on its own. Regenerated apiDump. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * refactor(extend-app-start): Name app start branching booleans by intent Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(extend-app-start): Make shouldSendStartMeasurements volatile It is written without a lock in onAppStartSpansSent() and read across threads in canExtendAppStart() (via extendAppStart()), with no happens-before edge. volatile guarantees visibility, matching the AtomicInteger/AtomicBoolean siblings in the same check. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * docs(extend-app-start): Explain why AppStartExtension holds span and transaction Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * feat(extend-app-start): Return null from getExtendedAppStartSpan when inactive Mirrors Sentry.getSpan(): the extender reports null when there is no active extended span instead of a NoOpSpan, so callers can tell there is no span running. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * feat(extend-app-start): Return null from AppStartExtension.getExtendedAppStartSpan when inactive Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * test(extend-app-start): Assert null extended span now that getExtendedAppStartSpan is nullable Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * feat(extend-app-start): Make Sentry.getExtendedAppStartSpan() nullable Mirrors Sentry.getSpan(): returns null when no extension is active instead of a NoOpSpan, so callers can tell there is no span running. Sample null-checks the span. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]> Co-authored-by: Sentry Github Bot <[email protected]> * changelog --------- Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]> Co-authored-by: Sentry Github Bot <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR Stack (Extend App Start)
📜 Description
Adds the public static facade for the app start extension API — the only PR in the stack that adds public API surface. Each method is a thin bridge to
getCurrentScopes().getOptions().getAppStartExtender()(theIAppStartExtenderfrom[1/4]), mirroringSentry.reportFullyDisplayed().Sentry.extendAppStart()Application.onCreate, right afterSentryAndroid.init).Sentry.getExtendedAppStartSpan()ISpanto attach child spans to — or aNoOpSpanwhen no extension is active.Sentry.finishExtendedAppStart()No-op when the SDK is disabled, on platforms without an app start extender, or when standalone app start tracing is off.
Also adds a usage example to the Android sample (
MyApplication):extendAppStart(), child spans undergetExtendedAppStartSpan(), thenfinishExtendedAppStart().💡 Motivation and Context
Fourth and final PR of the app start extension API stack (#5553), which lets apps extend the app start measurement to cover extra launch-time work — e.g. loading remote config before the first screen (mirrors sentry-cocoa's
extendAppLaunch()/finishExtendedAppLaunch()).💚 How did you test it?
SentryTestverifies the three methods delegate tooptions.getAppStartExtender(), and that the API returns a no-op span / no-ops when the SDK is disabled.apiCheck, spotless, and the:sentryunit suite pass; the sample module compiles.📝 Checklist
sendDefaultPIIis enabled.🔮 Next steps
Final PR of the stack. Once the stack is merged up, the collection branch (#5604) squash-merges into
main.#skip-changelog