◐ Shell
clean mode source ↗

[DNM] Thought experiment: Encapsulate standalone app start logic in AppStartIntegration by 0xadam-brown · Pull Request #5488 · getsentry/sentry-java

@0xadam-brown @cursoragent

…tReporter

The standalone app-start feature was spread across ActivityLifecycleIntegration,
AppStartMetrics, and PerformanceAndroidEventProcessor: ALI held the transaction
field, creation branches, and the headless listener callback; AppStartMetrics
acted as a trace-id mailbox; and the event processor re-derived "is this a
headless standalone app start" by sniffing the presence of a span-data key. No
single place owned the feature.

Introduce StandaloneAppStartReporter, which owns the feature end-to-end on the
post-init side of the SDK (it holds an IScopes and can start transactions):
emitting the activity-launch sibling and the headless transaction, keeping the
shared trace id, and classifying transactions for the event processor via
isStandaloneAppStart/isHeadlessAppStart. AppStartMetrics stays a pre-init,
scope-less recorder that only emits the headless signal; ALI delegates to the
reporter instead of carrying the logic; the event processor asks the reporter
how to classify a transaction rather than poking at its data map. Only data
crosses the pre-/post-init seam, not logic.

No behavior change. Net -76 lines and two fewer public methods on AppStartMetrics.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

ref(android): Consolidate headless app start scheduling to one site

Scheduling of the main-thread idle check lived in two places: registerLifecycleCallbacks
(gated on appStartType == UNKNOWN || listener != null) and setHeadlessAppStartListener
(a side-effect gated on isCallbackRegistered && no activities yet && first draw not done).
The setter's side-effect was load-bearing because the listener is installed during SDK
init, after the early SentryPerformanceProvider registration, so on API 35+ with a known
start type registerLifecycleCallbacks would skip scheduling and the setter had to cover it.

Schedule the idle check unconditionally from the single registration site and make the
setter a plain assignment. The idle handler already no-ops once an Activity exists and reads
the listener when it fires (after init has installed it), so it can serve both consumers —
the pre-API-35 cold/warm heuristic and standalone headless emission — without the setter
needing to know about timing.

The only behavior change: on API 35+ with a known type and standalone disabled, the idle
check is now scheduled (previously skipped). It no-ops for foreground launches and, for a
truly headless launch, correctly marks the start as background and stops app-start profilers.

ref(android): Add AppStartIntegration for standalone app start

Move headless standalone app-start emission out of AppStartMetrics into
StandaloneAppStartReporter behind a new AppStartIntegration. Metrics keeps
idle scheduling and shared boot bookkeeping; ActivityLifecycleIntegration
coordinates via StandaloneAppStartCoordinator only when the feature is enabled.

Refs #5342
Co-Authored-By: Cursor <cursoragent@cursor.com>

Co-authored-by: Cursor <cursoragent@cursor.com>

ref(android): Decouple app start from ActivityLifecycleIntegration

ActivityLifecycleIntegration dispatches AppStartLifecycle callbacks instead
of taking a StandaloneAppStartCoordinator. AppStartIntegration registers
StandaloneAppStartReporter as the listener at init time.

Refs #5342
Co-Authored-By: Cursor <cursoragent@cursor.com>

Co-authored-by: Cursor <cursoragent@cursor.com>

ref(android): Plan first ui.load via standalone app start listener

Use UiLoadStartPlan so ActivityLifecycleIntegration starts ui.load without
headless branching while StandaloneAppStartReporter owns trace reuse and
sibling App Start emission.

Co-Authored-By: Cursor <cursoragent@cursor.com>

ref(android): Use single ui.load startTransaction path for standalone

planFirstUiLoad now takes isFirstProcessStart so ActivityLifecycleIntegration
always builds one TransactionContext while headless reuse stays in the reporter.

Co-Authored-By: Cursor <cursoragent@cursor.com>

ref(android): Rename FirstUiLoad types and narrow standalone API

Rename AppStartLifecycle and AppStartLifecycleListener to FirstUiLoad and
FirstUiLoadListener. Make standalone app start wiring package-private and
drop those types from the public api dump.

Co-Authored-By: Cursor <cursoragent@cursor.com>

ref(android): Replace static FirstUiLoad with init-scoped coordinator

Wire the standalone app-start listener through a FirstUiLoadCoordinator
created in AndroidOptionsInitializer and shared by AppStartIntegration
and ActivityLifecycleIntegration instead of a static global holder.

Co-Authored-By: Auto <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>