You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
SentryFrameMetricsCollector blocks the main thread in HandlerThread.getLooper() when the first activity transaction starts #6443
Use sentry-android 8.60.0 (same code in 8.61.0) with enablePerformanceV2 and enableFramesTracking on (the defaults) and activity tracing on.
Cold start the app on a busy device, for example shortly after boot.
Record a Perfetto trace of the cold start.
Look at the main thread during the launcher activity's onCreate: it sits in HandlerThread.getLooper() under SentryFrameMetricsCollector.startCollection, called from SpanFrameMetricsCollector.onSpanStarted when ActivityLifecycleIntegration starts the activity transaction.
Expected Result
Starting frame collection for the first activity transaction doesn't block the main thread.
Actual Result
SentryFrameMetricsCollector.ensureHandlerThreadStarted() starts the HandlerThread and then calls getLooper(), which blocks the main thread until the new thread is running. In our trace this took 308 ms during the launcher activity's onCreate. Since #5641 (8.47.0) this wait happens at the first startCollection() call instead of during SentryAndroid.init, so it moved to Activity.onCreate rather than going away.
Probably the main cause is that ensureHandlerThreadStarted() calls handlerThread.start() and then getLooper(), which waits until the new thread has run.
One potential solution is to keep starting the thread lazily, but stop calling getLooper() on the calling thread, then create the Handler in HandlerThread.onLooperPrepared(). It runs on the new thread once its looper exists, so nothing waits.
The only trad-off I can think off is that the listener is only attached before the activity's first frame if the thread is ready by the time that frame is scheduled, it means that if the thread is slower, we might miss some transactions.
I that frame drops are critical, this proposal may require further consideration
Activity
linear-code commentedon Nov 2, 2026
JAVA-821
ikerfah commentedon Nov 3, 2026
Probably the main cause is that
ensureHandlerThreadStarted()callshandlerThread.start()and thengetLooper(), which waits until the new thread has run.One potential solution is to keep starting the thread lazily, but stop calling
getLooper()on the calling thread, then create theHandlerinHandlerThread.onLooperPrepared(). It runs on the new thread once its looper exists, so nothing waits.The only trad-off I can think off is that the listener is only attached before the activity's first frame if the thread is ready by the time that frame is scheduled, it means that if the thread is slower, we might miss some transactions.
I that frame drops are critical, this proposal may require further consideration