feat(android): Add standalone app start tracing#5342
Draft
feat(android): Add standalone app start tracing#5342
Conversation
Introduce experimental `enableStandaloneAppStartTracing` option that creates a separate app start transaction instead of attaching app start as a child span of the first activity transaction. This is the happy path only (foreground importance, activity launch, first frame drawn as end time). The standalone transaction shares the same trace ID as the activity transaction but is not bound to the scope. App start measurements and child spans (process init, content providers, application.onCreate) are attached to the standalone transaction instead of the activity transaction. Includes foreground importance check branching to prepare for the non-activity launch path (next PR). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the app starts without launching an activity (service, broadcast receiver, content provider), create a standalone app start transaction with the end time determined by priority: 1. onApplicationPostCreate (Gradle plugin bytecode instrumentation) 2. ApplicationStartInfo timestamps (API 35+) 3. firstIdle - main thread idle handler (pre-API 35 fallback) The non-activity app start transaction stores its trace ID so that if an activity is later launched, the activity transaction reuses the same trace ID to keep both in the same trace. Adds OnNoActivityStartedListener callback from AppStartMetrics to ActivityLifecycleIntegration, triggered by checkCreateTimeOnMain() when no activity was created after Application.onCreate(). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…entation When an app is launched via broadcast receiver, service, or content provider (no activity), detect this via Handler.post() and create a standalone app start transaction. Resolves app start end time with priority: Gradle plugin > ApplicationStartInfo (API 35+) > process init time. Also attaches child spans (process init, content providers, Application.onCreate) to standalone transactions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract the "try appStartSpan, fall back to sdkInitTimeSpan" logic used for standalone (non-activity) app start transactions into a new AppStartMetrics.getAppStartTimeSpanDirect() helper, removing the duplicated inline fallback in ActivityLifecycleIntegration and the private helper in PerformanceAndroidEventProcessor. Also cache the API 35+ ApplicationStartInfo on registerLifecycleCallbacks so onAppStartSpansSent no longer re-queries ActivityManager, and simplify the non-activity detection path to always use the main-thread IdleHandler. Regenerates the sentry-android-core API to include method additions missed in prior commits on this branch (standalone-app-start options, trace id accessors, OnNoActivityStartedListener). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires up the TestBroadcastReceiver added earlier so the sample app can trigger a non-activity cold start via `adb shell am broadcast`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tart-tracing # Conflicts: # sentry-android-core/src/main/java/io/sentry/android/core/performance/AppStartMetrics.java
…icate emission Two pre-merge fixes for the standalone app-start tracing path introduced on this branch (issue #5046): - AppStartMetrics.checkCreateTimeOnMain() now defaults appStartType to COLD when UNKNOWN with no active activities. On API < 35 (where ApplicationStartInfo is unavailable) non-activity cold starts were stuck at UNKNOWN, which both misclassified the standalone transaction as App Start Warm and caused PerformanceAndroidEventProcessor.attachAppStartSpans to early-return (dropping process.load / application.load / contentprovider.load phase spans). - ActivityLifecycleIntegration.onActivityPreCreated() now skips emitting a second standalone App Start transaction when the non-activity path has already reported the process's app start (detected via the stashed appStartTraceId). Previously a broadcast followed by an activity launch produced two standalone transactions (a spurious App Start Warm in addition to the broadcast's App Start Cold), violating one-per-process semantics. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
buenaflor
commented
Apr 28, 2026
Comment on lines
+177
to
+184
| /** Trace ID from a non-activity app start transaction, to be reused by a later activity. */ | ||
| public @Nullable SentryId getAppStartTraceId() { | ||
| return appStartTraceId; | ||
| } | ||
|
|
||
| public void setAppStartTraceId(final @Nullable SentryId traceId) { | ||
| this.appStartTraceId = traceId; | ||
| } |
Contributor
Author
There was a problem hiding this comment.
later on this should generally be fixed to be trace connected by session and not only the app start + ui.load transactions
Contributor
|
📲 Install BuildsAndroid
|
Contributor
Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 9770665 | 315.64 ms | 378.00 ms | 62.36 ms |
| cf708bd | 434.73 ms | 502.96 ms | 68.22 ms |
| d501a7e | 348.06 ms | 431.42 ms | 83.36 ms |
| 72020f8 | 312.32 ms | 370.94 ms | 58.62 ms |
| b750b96 | 421.25 ms | 444.09 ms | 22.84 ms |
| 319f256 | 317.53 ms | 370.83 ms | 53.29 ms |
| c8125f3 | 397.65 ms | 485.14 ms | 87.49 ms |
| 55aaf9b | 310.45 ms | 352.56 ms | 42.12 ms |
| b8bd880 | 314.56 ms | 336.50 ms | 21.94 ms |
| f064536 | 349.86 ms | 417.66 ms | 67.80 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 9770665 | 0 B | 0 B | 0 B |
| cf708bd | 1.58 MiB | 2.11 MiB | 539.71 KiB |
| d501a7e | 0 B | 0 B | 0 B |
| 72020f8 | 1.58 MiB | 2.19 MiB | 620.21 KiB |
| b750b96 | 1.58 MiB | 2.10 MiB | 533.20 KiB |
| 319f256 | 1.58 MiB | 2.19 MiB | 619.79 KiB |
| c8125f3 | 1.58 MiB | 2.10 MiB | 532.32 KiB |
| 55aaf9b | 0 B | 0 B | 0 B |
| b8bd880 | 1.58 MiB | 2.29 MiB | 722.92 KiB |
| f064536 | 1.58 MiB | 2.20 MiB | 633.90 KiB |
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.
📜 Description
Adds standalone Android app-start transaction support behind the opt-in
enableStandaloneAppStartTracingoption and theio.sentry.standalone-app-start-tracing.enablemanifest key.This PR also covers the non-activity startup path so cold starts triggered by broadcasts or foreground services can emit an
App Start Cold/Warmtransaction without waiting for an activity. The legacy flag-off behavior is preserved: activity app-start data remains nested under theui.loadtransaction.💡 Motivation and Context
Resolves: #5046
Standalone app-start traces make app-start performance visible independently from activity load transactions, and they let Android report app starts where no activity is launched. The implementation keeps the default behavior unchanged and adds tests for the new option, manifest metadata, transaction context trace reuse, app-start metrics, event processing, and activity lifecycle behavior.
💚 How did you test it?
./gradlew spotlessApply apiDumpstandalone_app_start_report.md:End-to-end Sentry verification
ui.load.app.start+activity.loadx2 +process.load+application.load; siblingui.loadd8e97ed6...ui.load->app.start.cold->process.load+activity.loadx2970768dc...app.start+process.load+application.load; noui.loada39fb47f...ApplicationStartInfo.app.start+process.load; noapplication.loadorui.loade100abb8...app.start+process.load; classified Cold5fb8e75d...app.start+process.load+application.load; noui.loadf8ad7a62...app.start+ siblingui.load; one standalone only04ffca08...📝 Checklist
sendDefaultPIIis enabled.🔮 Next steps