feat(extend-app-start): [3/4] Eagerly create the extended app start transaction#5608
feat(extend-app-start): [3/4] Eagerly create the extended app start transaction#5608buenaflor wants to merge 22 commits into
Conversation
|
📲 Install BuildsAndroid
|
Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| db5be2f | 311.84 ms | 365.94 ms | 54.10 ms |
| 388ffc5 | 315.18 ms | 362.43 ms | 47.25 ms |
| 682e090 | 347.33 ms | 427.28 ms | 79.95 ms |
| b76203f | 336.02 ms | 403.42 ms | 67.40 ms |
| ca0c0cf | 318.45 ms | 370.06 ms | 51.61 ms |
| 201a6fd | 326.00 ms | 371.92 ms | 45.92 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| db5be2f | 0 B | 0 B | 0 B |
| 388ffc5 | 0 B | 0 B | 0 B |
| 682e090 | 0 B | 0 B | 0 B |
| b76203f | 0 B | 0 B | 0 B |
| ca0c0cf | 0 B | 0 B | 0 B |
| 201a6fd | 0 B | 0 B | 0 B |
Previous results on branch: feat/app-start-extension-materialize
Startup times
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 4451634 | 321.21 ms | 376.21 ms | 55.00 ms |
| 4cf89ba | 340.51 ms | 416.92 ms | 76.41 ms |
| 44c06ea | 324.19 ms | 384.88 ms | 60.69 ms |
| 20bbe9a | 305.48 ms | 387.25 ms | 81.77 ms |
| 69caaee | 313.73 ms | 373.92 ms | 60.19 ms |
| 3a27919 | 371.17 ms | 447.31 ms | 76.14 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 4451634 | 0 B | 0 B | 0 B |
| 4cf89ba | 0 B | 0 B | 0 B |
| 44c06ea | 0 B | 0 B | 0 B |
| 20bbe9a | 0 B | 0 B | 0 B |
| 69caaee | 0 B | 0 B | 0 B |
| 3a27919 | 0 B | 0 B | 0 B |
54be119 to
d2b96ad
Compare
0f8ee4d to
5dfb0e1
Compare
5dfb0e1 to
0bd259a
Compare
5dd39af to
3bc1643
Compare
0bd259a to
0b6ada0
Compare
3bc1643 to
45f6c0b
Compare
c3f3627 to
cbaef2f
Compare
…tion (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) <noreply@anthropic.com>
2d5188e to
01b1dc4
Compare
cbaef2f to
d996425
Compare
…on extendAppStart Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… coverage Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nd trim comments Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…xtension-materialize
…app start path Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…xtension-materialize
…xtension-materialize
There was a problem hiding this comment.
Pull request overview
Implements end-to-end wiring for the Android “extend app start” feature by registering an extend listener (standalone-only), eagerly creating/holding open a standalone app.start transaction + extended child span, and updating app start vital calculation logic to account for the extension (including deadline suppression).
Changes:
ActivityLifecycleIntegrationregisters the extend listener when standalone app start tracing is enabled, eagerly creates the standaloneapp.starttransaction +app.start.extended_app_startspan on extension, and continues the trace intoui.loadwhile attaching the first activity screen to the eager transaction.PerformanceAndroidEventProcessorcomputes the app start measurement asmax(natural first-frame duration, extended end)when extended, and suppresses the measurement entirely on deadline while still attaching app start spans.AppStartExtensionaddsisExtended()(processor gate) andsetData(...)(attach screen name to the eager transaction once known), with expanded unit test coverage.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java | Registers extend listener (standalone-only), eagerly creates standalone app.start + extended span, continues trace into ui.load, and finishes the eager txn at first-frame/headless end while waiting for children. |
| sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java | Adjusts app start measurement logic for extended flows (never shorten; suppress on deadline) while still finalizing/attaching app start spans. |
| sentry-android-core/src/main/java/io/sentry/android/core/AppStartExtension.java | Adds setData for eager txn mutation and isExtended for processor gating. |
| sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt | Adds tests covering eager standalone app.start creation, trace continuation into ui.load, screen attachment, wait-for-children behavior, standalone-off no-op, and survival across activity destroy. |
| sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt | Adds tests for extended end driving cold measurement, never reporting shorter than natural duration, and deadline suppression behavior. |
| sentry-android-core/src/test/java/io/sentry/android/core/AppStartExtensionTest.kt | Adds test ensuring isExtended remains true after the transaction finishes. |
| sentry-android-core/api/sentry-android-core.api | API dump updated for new AppStartExtension methods (isExtended, setData). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
….extended Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…xtension-materialize
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) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 4 total unresolved issues (including 3 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f2d1c34. Configure here.
…n 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) <noreply@anthropic.com>
…t 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) <noreply@anthropic.com>
…xtension-materialize
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lize' into feat/app-start-extension-materialize # Conflicts: # sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java

PR Stack (Extend App Start)
📜 Description
Registers the extend-listener and wires the extension through to the trace and the vital, making the feature work end-to-end. The extension is eager and standalone-only — only the standalone
app.starttransaction is independent of an activity and can be created inApplication.onCreate.ActivityLifecycleIntegrationenableStandaloneAppStartTracingis on (this is what makes the API standalone-only). OnSentry.extendAppStart()the listener eagerly creates the standaloneapp.starttransaction (waitForChildren+ deadline) and itsapp.start.extendedchild, and returns them as anExtendedAppStart.PerformanceAndroidEventProcessormax(natural first-frame duration, extended end), so an early finish never shortens it. On deadline the measurement is suppressed entirely (no inflated ~30s value); spans still attach. Non-extended starts are unchanged.AppStartExtensionisExtended()(the processor's gate) andsetData(...)(to attach the screen once the first activity is known).Notes
appStartTransactionfield, so per-activity cleanup can't cancel it.ui.load(viaisActive()) instead of starting a secondapp.start, and attaches the screen so the foregroundapp.startkeeps itsapp.vitals.start.screen.waitForChildrenholds it open untilSentry.finishAppStart(). Headless finishes it at the headless stop time. A sharedcreateStandaloneAppStartTransaction(...)helper backs both paths.ui.load) extension path are removed — eager creation makes them unnecessary.💡 Motivation and Context
Part of the app start extension API stack (#5553). This is where the extension actually affects the trace and the vital, scoped to standalone app start tracing because only the standalone
app.starttransaction can be created eagerly inonCreate.💚 How did you test it?
Unit tests (TDD):
ActivityLifecycleIntegrationTest— eagerapp.start+ extended child on extend; first activity continues the trace with no secondapp.startand the screen attached; standalone & headless stay open untilfinishAppStart(); standalone-off is a no-op; the eager txn survives activity destroy.PerformanceAndroidEventProcessorTest— extended end drives the cold measurement; an early finish never reports shorter than the first-frame duration; a deadline finish suppresses the measurement.apiCheck, spotless, and the:sentry-android-coreunit suite pass.📝 Checklist
sendDefaultPIIis enabled.🔮 Next steps
[4/4]— add the publicSentry.extendAppStart()/Sentry.finishAppStart()/Sentry.getExtendedAppStartSpan()facade.#skip-changelog