Skip to content

perf(android): Move start-reason binder off main thread (JAVA-616) - #5866

Draft
runningcode wants to merge 2 commits into
mainfrom
no/java-616-defer-start-reasons-off-main
Draft

perf(android): Move start-reason binder off main thread (JAVA-616)#5866
runningcode wants to merge 2 commits into
mainfrom
no/java-616-defer-start-reasons-off-main

Conversation

@runningcode

Copy link
Copy Markdown
Contributor

📜 Description

On API 35+, AppStartMetrics.registerLifecycleCallbacks performed an ActivityManager.getHistoricalProcessStartReasons(1) binder call synchronously on the main thread. Under auto-init this runs at ContentProvider time (SentryPerformanceProvider.onAppLaunched), blocking the main thread on a system_server round-trip during the coldest part of app start.

This moves the lookup onto a short-lived background daemon thread. The Sentry executor doesn't exist this early (ContentProvider time), so a plain thread is used. cachedStartInfo is now volatile and consumed best-effort:

  • Cold/warm classification moves into onActivityCreated: it prefers the resolved ApplicationStartInfo and falls back to the existing pre-API-35 heuristic if the lookup hasn't resolved yet.
  • The headless-start path consults the resolved start info before defaulting to COLD.
  • getAppStartReason() returns null if the lookup hasn't resolved by the time it is read.

No public API change (apiDump clean).

💡 Motivation and Context

JAVA-616. Fixes #5702.

The binder call stalled the main thread at the most latency-sensitive moment of startup. Measured on a Pixel 10 (API 36) across cold starts, the synchronous call cost ~0.5–2.2 ms of main-thread time (median ~0.7 ms, mean ~0.9 ms). Deferring it reclaims that time from the main thread.

💚 How did you test it?

  • AppStartMetricsTestApi35 unit tests updated to inject a synchronous executor and drive onActivityCreated; all pass.
  • On-device (Pixel 10, API 36, cold starts via force-stop → launch): before ≈ 0.5–2.2 ms on the main thread (median ~0.7 ms); after, the same work runs on the background thread and cachedStartInfo was ready before the first activity in every observed cold start — even with the sample's artificial start-padding removed — so cold/warm and app.start.reason are preserved in practice.

📝 Checklist

  • I added GH Issue ID & Linear ID
  • I added tests to verify the changes.
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled.
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • Review from the native team if needed.
  • No breaking change or entry added to the changelog.
  • No breaking change for hybrid SDKs or communicated to hybrid SDKs.

🔮 Next steps

  • Consider a follow-up to reuse a shared background executor once one is available that early, instead of a one-off thread.

getHistoricalProcessStartReasons is an API 35+ binder round-trip to
system_server that ran synchronously on the main thread at ContentProvider
time, stalling the coldest part of app start. Run it on a background thread
instead. The result is consumed best-effort: cold/warm falls back to the
pre-API-35 heuristic and app.start.reason is null if the lookup has not
resolved by the first activity.
@linear-code

linear-code Bot commented Jul 29, 2026

Copy link
Copy Markdown

JAVA-616

@sentry

sentry Bot commented Jul 29, 2026

Copy link
Copy Markdown

📲 Install Builds

Android

🔗 App Name App ID Version Configuration
SDK Size io.sentry.tests.size 8.50.0 (1) release

⚙️ sentry-android Build Distribution Settings

@github-actions

Copy link
Copy Markdown
Contributor

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 320.28 ms 373.06 ms 52.78 ms
Size 0 B 0 B 0 B

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
6b019b7 319.84 ms 333.15 ms 13.31 ms
c3ee041 310.64 ms 361.90 ms 51.26 ms
d501a7e 348.06 ms 431.42 ms 83.36 ms
3998a95 415.94 ms 478.54 ms 62.60 ms
ed33deb 343.30 ms 362.41 ms 19.10 ms
5b1a06b 310.56 ms 362.79 ms 52.22 ms
91bb874 310.68 ms 359.24 ms 48.56 ms
0ee65e9 317.37 ms 366.50 ms 49.13 ms
6edfca2 305.52 ms 432.78 ms 127.26 ms
5e269de 292.83 ms 379.12 ms 86.29 ms

App size

Revision Plain With Sentry Diff
6b019b7 0 B 0 B 0 B
c3ee041 0 B 0 B 0 B
d501a7e 0 B 0 B 0 B
3998a95 1.58 MiB 2.10 MiB 532.96 KiB
ed33deb 1.58 MiB 2.13 MiB 559.52 KiB
5b1a06b 0 B 0 B 0 B
91bb874 1.58 MiB 2.13 MiB 559.07 KiB
0ee65e9 0 B 0 B 0 B
6edfca2 1.58 MiB 2.13 MiB 559.07 KiB
5e269de 0 B 0 B 0 B

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Defer the API 35+ getHistoricalProcessStartReasons binder call off the main thread

1 participant