Skip to content

fix(android-fragment): support detach/attach navigation in fragment tracing#5660

Open
romtsn wants to merge 7 commits into
mainfrom
fix/fragment-detach-attach-tracing
Open

fix(android-fragment): support detach/attach navigation in fragment tracing#5660
romtsn wants to merge 7 commits into
mainfrom
fix/fragment-detach-attach-tracing

Conversation

@romtsn

@romtsn romtsn commented Jun 29, 2026

Copy link
Copy Markdown
Member

Summary

Fixes a silent data quality issue where detach/attach tab navigation (and similar patterns) left ui.load spans open until the 30-second activity transaction deadline, producing badly inflated performance data.

Root cause

SentryFragmentLifecycleCallbacks started spans in onFragmentCreated and stopped them in onFragmentStarted (a concession to ViewPager2 which locks background fragments to STARTED). For detach/attach navigation — manual tab switching (transaction.detach(current).attach(next).commit()), ViewPager v1 with FragmentPagerAdapter, and custom navigation frameworks that preserve fragment state — onFragmentCreated is never called for off-screen fragments. Spans would hang silently until the deadline.

Fix

Three targeted changes, all leveraging the fact that startTracing / stopTracing are already idempotent:

Hook Change Why
onFragmentViewCreated +startTracing Enables narrower viewCreated → resumed span for detach/attach paths. No-op if onFragmentCreated already started a span.
onFragmentResumed +stopTracing Ends the span on the detach/attach path where onFragmentStarted may not fire. No-op for the normal path where onFragmentStarted already closed the span.
onFragmentViewDestroyed +stopTracing Failsafe: closes any span for a fragment whose view is destroyed before reaching STARTED or RESUMED.

The normal lifecycle path (onFragmentCreated → onFragmentViewCreated → onFragmentStarted → onFragmentResumed) is completely unaffected — still produces exactly one ui.load span, closed at onFragmentStarted.

What's new for customers

Apps using detach/attach navigation will now see correct, narrower ui.load spans (viewCreated → resumed) instead of timeout-inflated spans.

Tests

Five new unit tests covering:

  • Detach/attach path: onFragmentViewCreated starts a span when tracing enabled
  • Normal path: onFragmentViewCreated is a no-op when span already running
  • Detach/attach path: onFragmentResumed stops the span
  • Normal path: onFragmentResumed does not double-finish after onFragmentStarted
  • Failsafe: onFragmentViewDestroyed stops a span that never reached STARTED/RESUMED

Example trace: https://sentry-sdks.sentry.io/explore/traces/trace/edb7de2bba454d70a4c13eee5bce660c/

As you can see TabFragmentB now correctly shows up after re-attaching (but wouldn't have without this fix):
Google Chrome 2026-06-30 17 25 15

Not verified

./gradlew spotlessApply apiDump could not run (no JDK in sandbox) — CI will apply formatting and regenerate .api files if needed.

…racing

For detach/attach tab navigation (manual tab switching, ViewPager v1 with
FragmentPagerAdapter, custom navigation frameworks), onFragmentCreated is
skipped for off-screen fragments that are re-attached. Previously this left
ui.load spans open until the 30s activity transaction deadline, producing
inflated performance data.

Fix by calling startTracing in onFragmentViewCreated as well as
onFragmentCreated. startTracing is idempotent (no-op if a span is already
running), so the normal onFragmentCreated -> onFragmentViewCreated path is
unaffected. Add matching stopTracing calls in onFragmentResumed (covers the
detach/attach path where onFragmentStarted may be skipped) and
onFragmentViewDestroyed (failsafe for fragments destroyed before reaching
STARTED or RESUMED). stopTracing is also idempotent, so the normal path is
unaffected.

Co-Authored-By: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
@sentry

sentry Bot commented Jun 29, 2026

Copy link
Copy Markdown

📲 Install Builds

Android

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

⚙️ sentry-android Build Distribution Settings

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 364.04 ms 473.08 ms 109.04 ms
Size 0 B 0 B 0 B

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
abfcc92 337.38 ms 427.39 ms 90.00 ms
4e3e79d 369.55 ms 418.39 ms 48.83 ms
9054d65 330.94 ms 403.24 ms 72.30 ms
a416a65 316.52 ms 359.67 ms 43.15 ms
d15471f 315.61 ms 360.22 ms 44.61 ms
22f4345 325.23 ms 454.66 ms 129.43 ms
6b019b7 403.90 ms 546.09 ms 142.19 ms
d217708 409.83 ms 474.72 ms 64.89 ms
52feca7 314.77 ms 378.67 ms 63.90 ms
f634d01 375.06 ms 420.04 ms 44.98 ms

App size

Revision Plain With Sentry Diff
abfcc92 1.58 MiB 2.13 MiB 557.31 KiB
4e3e79d 0 B 0 B 0 B
9054d65 1.58 MiB 2.29 MiB 723.38 KiB
a416a65 1.58 MiB 2.12 MiB 555.26 KiB
d15471f 1.58 MiB 2.13 MiB 559.54 KiB
22f4345 1.58 MiB 2.29 MiB 719.83 KiB
6b019b7 0 B 0 B 0 B
d217708 1.58 MiB 2.10 MiB 532.97 KiB
52feca7 0 B 0 B 0 B
f634d01 1.58 MiB 2.10 MiB 533.40 KiB

Previous results on branch: fix/fragment-detach-attach-tracing

Startup times

Revision Plain With Sentry Diff
c53e6fb 312.80 ms 372.79 ms 59.99 ms
be2bf5f 314.90 ms 364.90 ms 50.00 ms
7966377 324.00 ms 379.38 ms 55.38 ms

App size

Revision Plain With Sentry Diff
c53e6fb 0 B 0 B 0 B
be2bf5f 0 B 0 B 0 B
7966377 0 B 0 B 0 B

romtsn and others added 3 commits June 30, 2026 15:52
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@romtsn romtsn marked this pull request as ready for review June 30, 2026 15:26
romtsn and others added 2 commits June 30, 2026 19:25
…ment

onFragmentCreated is skipped during detach/attach navigation, so the
screen name was never updated for re-attached fragments. Mirror the
screen tracking into onFragmentViewCreated to cover that path.

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

@0xadam-brown 0xadam-brown left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! A few quick comments; otherwise lgtm 👍

// off-screen fragments that are re-attached. Starting here enables a narrower
// "view created -> resumed" span for those paths. startTracing is idempotent, so for the
// normal onFragmentCreated -> onFragmentViewCreated path this is a no-op.
if (fragment.isAdded) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: We could internalize the if (fragment.isAdded) {... checks inside startTracing() since they protect the integrity of ui.load initiation + we always apply them.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it also make sense to add the scopes.options.isEnableScreenTracking inside there too?

import androidx.fragment.app.Fragment
import androidx.fragment.app.commit

class DetachAttachTabsActivity : AppCompatActivity(R.layout.activity_detach_attach_tabs) {

@0xadam-brown 0xadam-brown Jul 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the sample 💯

m: Unfortunately I'm not able to see or click the tabs on my emulator (Pixel 10, API 36.1):

Image

...seems they're being rendered under the action bar.

@runningcode runningcode left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fragments are so confusing! Thanks for the fix.

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.

4 participants