fix(replay): Keep replay recording during animations#5489
Open
romtsn wants to merge 5 commits into
Open
Conversation
Skip only the first unstable PixelCopy capture, then continue emitting frames while the screen keeps invalidating. This prevents animated screens from freezing Session Replay visuals while preserving the existing debounce for one-off redraws. Fixes GH-5404 Co-Authored-By: Codex <noreply@openai.com>
Add separate Android sample screens for Lottie, Compose canvas, and classic View animations so replay capture behavior can be tested manually. Keep the sample app on the Canvas replay screenshot strategy while exercising these animations. Refs GH-5404 Co-Authored-By: Codex <noreply@openai.com>
e5fa288 to
05247b6
Compare
📲 Install BuildsAndroid
|
Contributor
Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 62b579c | 318.48 ms | 367.71 ms | 49.24 ms |
| b6702b0 | 395.86 ms | 409.98 ms | 14.12 ms |
| f064536 | 327.04 ms | 405.35 ms | 78.31 ms |
| 6b019b7 | 403.90 ms | 546.09 ms | 142.19 ms |
| 2195398 | 344.65 ms | 403.96 ms | 59.30 ms |
| ce0a49e | 532.00 ms | 609.96 ms | 77.96 ms |
| 27d7cf8 | 306.76 ms | 366.66 ms | 59.90 ms |
| 22f4345 | 307.87 ms | 354.51 ms | 46.64 ms |
| ad8da22 | 314.52 ms | 352.47 ms | 37.95 ms |
| e59e22a | 374.68 ms | 442.14 ms | 67.46 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 62b579c | 0 B | 0 B | 0 B |
| b6702b0 | 1.58 MiB | 2.12 MiB | 551.79 KiB |
| f064536 | 1.58 MiB | 2.20 MiB | 633.90 KiB |
| 6b019b7 | 0 B | 0 B | 0 B |
| 2195398 | 0 B | 0 B | 0 B |
| ce0a49e | 1.58 MiB | 2.10 MiB | 532.94 KiB |
| 27d7cf8 | 1.58 MiB | 2.12 MiB | 549.42 KiB |
| 22f4345 | 1.58 MiB | 2.29 MiB | 719.83 KiB |
| ad8da22 | 1.58 MiB | 2.29 MiB | 719.83 KiB |
| e59e22a | 1.58 MiB | 2.20 MiB | 635.34 KiB |
Use ContextCompat.getColor in ReplayAnimationsActivity so release lint passes with the sample app minSdk. Refs GH-5489 Co-Authored-By: Codex <noreply@openai.com>
romtsn
commented
Jun 2, 2026
| ) : ScreenshotStrategy { | ||
|
|
||
| private companion object { | ||
| const val MAX_UNSTABLE_CAPTURES_TO_SKIP = 1 |
Member
Author
There was a problem hiding this comment.
could've been a boolean, but I decided that a counter is more natural and we may want to increase it in the future if we see problems with this
0xadam-brown
approved these changes
Jun 2, 2026
Member
0xadam-brown
left a comment
There was a problem hiding this comment.
Implementation looks good.
Any concerns about masking slips during animation?
Nicely, I see our docs flag the possibility:
This strategy has slightly lower performance overhead but may result in masking misalignments due to the asynchronous nature of the PixelCopy API. We recommend using this strategy for apps that do not have strict PII requirements or do not require masking functionality.
Not sure if we want the extra security, but we could also consider:
- increasing the mask size when recording unstable captures, or
- covering the view's entire range of possible positions during the unstable window, ie, the union of where it was (in the pixels, time T) and where the hierarchy thinks it is (T+delta).
Up to you.
Document why PixelCopyStrategy caps skipped unstable captures so continuous animations keep producing replay frames. Refs GH-5489 Co-Authored-By: Codex <noreply@openai.com>
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
Update Session Replay's PixelCopy strategy so continuously invalidating screens do not freeze after the first unstable capture. The strategy now skips only the first unstable PixelCopy/view-hierarchy pair, then resumes emitting frames while the screen keeps drawing.
Add Android sample screens for Lottie, Compose canvas, and classic View animations so the behavior can be tested manually in the sample app. The sample app remains configured to use the Canvas replay screenshot strategy.
💡 Motivation and Context
Continuous animations can cause PixelCopy output and the view hierarchy snapshot to be taken from slightly different moments. The previous debounce treated this as persistent instability and stopped emitting updated frames, which made replay visuals freeze on animated screens.
Fixes #5404
💚 How did you test it?
./gradlew :sentry-android-replay:testDebugUnitTest --tests io.sentry.android.replay.screenshot.PixelCopyStrategyTest
./gradlew spotlessApply apiDump
./gradlew :sentry-samples:sentry-samples-android:installDebug
Example replay: https://sentry-sdks.sentry.io/explore/replays/e6d4d1d93cff44cead71d10ba9d54c59/?t=9
📝 Checklist
🔮 Next steps