fix(session-replay): Fixes orientation change misalignment for session replay on Android#5321
fix(session-replay): Fixes orientation change misalignment for session replay on Android#5321
Conversation
|
@romtsn I took the liberty of adding you as a reviewer too since you have more context on the internals of SR on Android 🙇 |
packages/core/android/src/main/java/io/sentry/react/RNSentryReactFragmentLifecycleTracer.java
Outdated
Show resolved
Hide resolved
Android (legacy) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| c94a927+dirty | 366.16 ms | 375.68 ms | 9.52 ms |
| 77061ed+dirty | 369.55 ms | 408.35 ms | 38.80 ms |
| 3bd3f0d+dirty | 447.21 ms | 472.31 ms | 25.10 ms |
| 93137d1+dirty | 400.15 ms | 424.74 ms | 24.59 ms |
| bc9680d | 375.15 ms | 401.12 ms | 25.97 ms |
| 7be1f99 | 454.83 ms | 461.36 ms | 6.53 ms |
| bfe454a+dirty | 573.44 ms | 579.46 ms | 6.02 ms |
| 955f2eb+dirty | 422.74 ms | 410.19 ms | -12.55 ms |
| 6a70a7e+dirty | 381.72 ms | 413.94 ms | 32.22 ms |
| 95aaf8a | 437.89 ms | 419.45 ms | -18.44 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| c94a927+dirty | 17.75 MiB | 19.70 MiB | 1.95 MiB |
| 77061ed+dirty | 17.75 MiB | 19.68 MiB | 1.94 MiB |
| 3bd3f0d+dirty | 17.75 MiB | 19.70 MiB | 1.95 MiB |
| 93137d1+dirty | 17.75 MiB | 19.70 MiB | 1.95 MiB |
| bc9680d | 17.75 MiB | 20.15 MiB | 2.41 MiB |
| 7be1f99 | 17.75 MiB | 20.15 MiB | 2.41 MiB |
| bfe454a+dirty | 17.75 MiB | 19.69 MiB | 1.94 MiB |
| 955f2eb+dirty | 17.75 MiB | 19.70 MiB | 1.95 MiB |
| 6a70a7e+dirty | 17.75 MiB | 19.69 MiB | 1.94 MiB |
| 95aaf8a | 17.75 MiB | 19.68 MiB | 1.93 MiB |
Previous results on branch: antonis/replay-orientation
Startup times
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| de62b18+dirty | 346.52 ms | 372.72 ms | 26.20 ms |
| d1e5478+dirty | 406.67 ms | 428.49 ms | 21.81 ms |
| 4c0adf8+dirty | 364.89 ms | 395.48 ms | 30.59 ms |
| c7e9027+dirty | 496.56 ms | 533.29 ms | 36.72 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| de62b18+dirty | 17.75 MiB | 19.75 MiB | 2.00 MiB |
| d1e5478+dirty | 43.75 MiB | 47.99 MiB | 4.23 MiB |
| 4c0adf8+dirty | 17.75 MiB | 19.75 MiB | 2.00 MiB |
| c7e9027+dirty | 43.75 MiB | 47.99 MiB | 4.23 MiB |
Android (new) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| d1fd647+dirty | 374.46 ms | 409.51 ms | 35.05 ms |
| 170d5ea+dirty | 348.79 ms | 406.94 ms | 58.15 ms |
| 6479fd5+dirty | 393.06 ms | 434.04 ms | 40.98 ms |
| 3e0a5f9+dirty | 379.92 ms | 450.96 ms | 71.04 ms |
| 1226664+dirty | 377.65 ms | 453.94 ms | 76.29 ms |
| 95aaf8a+dirty | 342.82 ms | 393.75 ms | 50.93 ms |
| 46da307+dirty | 356.62 ms | 415.02 ms | 58.40 ms |
| 5ee3314+dirty | 358.69 ms | 394.00 ms | 35.31 ms |
| 2adbd1e+dirty | 366.13 ms | 419.49 ms | 53.36 ms |
| 1853710+dirty | 360.67 ms | 396.28 ms | 35.61 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| d1fd647+dirty | 7.15 MiB | 8.43 MiB | 1.28 MiB |
| 170d5ea+dirty | 7.15 MiB | 8.42 MiB | 1.27 MiB |
| 6479fd5+dirty | 7.15 MiB | 8.41 MiB | 1.26 MiB |
| 3e0a5f9+dirty | 7.15 MiB | 8.42 MiB | 1.27 MiB |
| 1226664+dirty | 7.15 MiB | 8.46 MiB | 1.30 MiB |
| 95aaf8a+dirty | 7.15 MiB | 8.41 MiB | 1.26 MiB |
| 46da307+dirty | 7.15 MiB | 8.41 MiB | 1.26 MiB |
| 5ee3314+dirty | 7.15 MiB | 8.43 MiB | 1.28 MiB |
| 2adbd1e+dirty | 7.15 MiB | 8.43 MiB | 1.28 MiB |
| 1853710+dirty | 7.15 MiB | 8.41 MiB | 1.26 MiB |
Previous results on branch: antonis/replay-orientation
Startup times
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| de62b18+dirty | 293.96 ms | 339.25 ms | 45.29 ms |
| d1e5478+dirty | 374.04 ms | 395.37 ms | 21.32 ms |
| 4c0adf8+dirty | 312.60 ms | 365.30 ms | 52.70 ms |
| c7e9027+dirty | 371.88 ms | 388.36 ms | 16.48 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| de62b18+dirty | 7.15 MiB | 8.46 MiB | 1.31 MiB |
| d1e5478+dirty | 43.94 MiB | 48.81 MiB | 4.88 MiB |
| 4c0adf8+dirty | 7.15 MiB | 8.46 MiB | 1.31 MiB |
| c7e9027+dirty | 43.94 MiB | 48.81 MiB | 4.88 MiB |
iOS (legacy) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 9f211e3+dirty | 1218.80 ms | 1233.88 ms | 15.08 ms |
| 785ffb1+dirty | 1237.63 ms | 1240.50 ms | 2.87 ms |
| ec14be7+dirty | 1234.64 ms | 1245.54 ms | 10.90 ms |
| 955f2eb+dirty | 1235.06 ms | 1253.88 ms | 18.81 ms |
| 1226664+dirty | 1220.59 ms | 1221.61 ms | 1.02 ms |
| 2adbd1e+dirty | 1207.51 ms | 1218.98 ms | 11.47 ms |
| 4167e15+dirty | 1213.39 ms | 1222.50 ms | 9.11 ms |
| 6a70a7e+dirty | 1225.82 ms | 1230.79 ms | 4.98 ms |
| d1fd647+dirty | 1219.35 ms | 1233.18 ms | 13.83 ms |
| 05bef0e+dirty | 1221.02 ms | 1237.04 ms | 16.02 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 9f211e3+dirty | 2.63 MiB | 3.91 MiB | 1.28 MiB |
| 785ffb1+dirty | 2.63 MiB | 3.81 MiB | 1.18 MiB |
| ec14be7+dirty | 2.63 MiB | 3.98 MiB | 1.34 MiB |
| 955f2eb+dirty | 2.63 MiB | 3.98 MiB | 1.35 MiB |
| 1226664+dirty | 2.63 MiB | 4.00 MiB | 1.37 MiB |
| 2adbd1e+dirty | 2.63 MiB | 4.00 MiB | 1.36 MiB |
| 4167e15+dirty | 2.63 MiB | 4.00 MiB | 1.37 MiB |
| 6a70a7e+dirty | 2.63 MiB | 3.98 MiB | 1.34 MiB |
| d1fd647+dirty | 2.63 MiB | 3.99 MiB | 1.36 MiB |
| 05bef0e+dirty | 2.63 MiB | 3.99 MiB | 1.36 MiB |
Previous results on branch: antonis/replay-orientation
Startup times
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| de62b18+dirty | 1213.62 ms | 1226.20 ms | 12.58 ms |
| c7e9027+dirty | 1229.94 ms | 1225.36 ms | -4.57 ms |
| d1e5478+dirty | 1215.80 ms | 1211.24 ms | -4.57 ms |
| 4c0adf8+dirty | 1218.67 ms | 1226.43 ms | 7.76 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| de62b18+dirty | 2.63 MiB | 4.01 MiB | 1.38 MiB |
| c7e9027+dirty | 3.41 MiB | 4.57 MiB | 1.16 MiB |
| d1e5478+dirty | 3.41 MiB | 4.57 MiB | 1.16 MiB |
| 4c0adf8+dirty | 2.63 MiB | 4.01 MiB | 1.38 MiB |
iOS (new) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 9f211e3+dirty | 1215.38 ms | 1218.15 ms | 2.77 ms |
| 785ffb1+dirty | 1213.71 ms | 1213.37 ms | -0.35 ms |
| ec14be7+dirty | 1229.62 ms | 1230.53 ms | 0.91 ms |
| 955f2eb+dirty | 1225.78 ms | 1239.27 ms | 13.49 ms |
| 1226664+dirty | 1237.10 ms | 1245.27 ms | 8.16 ms |
| 2adbd1e+dirty | 1220.65 ms | 1230.20 ms | 9.56 ms |
| 4167e15+dirty | 1228.96 ms | 1242.15 ms | 13.19 ms |
| 6a70a7e+dirty | 1231.40 ms | 1239.49 ms | 8.09 ms |
| d1fd647+dirty | 1218.16 ms | 1225.82 ms | 7.65 ms |
| 05bef0e+dirty | 1223.31 ms | 1225.55 ms | 2.24 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 9f211e3+dirty | 3.19 MiB | 4.48 MiB | 1.29 MiB |
| 785ffb1+dirty | 3.19 MiB | 4.38 MiB | 1.19 MiB |
| ec14be7+dirty | 3.19 MiB | 4.54 MiB | 1.36 MiB |
| 955f2eb+dirty | 3.19 MiB | 4.55 MiB | 1.36 MiB |
| 1226664+dirty | 3.19 MiB | 4.57 MiB | 1.38 MiB |
| 2adbd1e+dirty | 3.19 MiB | 4.56 MiB | 1.38 MiB |
| 4167e15+dirty | 3.19 MiB | 4.57 MiB | 1.38 MiB |
| 6a70a7e+dirty | 3.19 MiB | 4.54 MiB | 1.36 MiB |
| d1fd647+dirty | 3.19 MiB | 4.56 MiB | 1.37 MiB |
| 05bef0e+dirty | 3.19 MiB | 4.56 MiB | 1.37 MiB |
Previous results on branch: antonis/replay-orientation
Startup times
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| de62b18+dirty | 1218.02 ms | 1216.85 ms | -1.17 ms |
| c7e9027+dirty | 1221.62 ms | 1214.66 ms | -6.96 ms |
| d1e5478+dirty | 1209.60 ms | 1215.76 ms | 6.16 ms |
| 4c0adf8+dirty | 1218.00 ms | 1228.30 ms | 10.30 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| de62b18+dirty | 3.19 MiB | 4.58 MiB | 1.39 MiB |
| c7e9027+dirty | 3.41 MiB | 4.57 MiB | 1.16 MiB |
| d1e5478+dirty | 3.41 MiB | 4.57 MiB | 1.16 MiB |
| 4c0adf8+dirty | 3.19 MiB | 4.58 MiB | 1.39 MiB |
|
important fix for pixel copy since it avoid leaks! |
|
@sentry review |
packages/core/android/src/main/java/io/sentry/react/RNSentryReactFragmentLifecycleTracer.java
Outdated
Show resolved
Hide resolved
# Conflicts: # CHANGELOG.md
|
@sentry review |
packages/core/android/src/main/java/io/sentry/react/RNSentryReactFragmentLifecycleTracer.java
Outdated
Show resolved
Hide resolved
packages/core/android/src/main/java/io/sentry/react/RNSentryReactFragmentLifecycleTracer.java
Outdated
Show resolved
Hide resolved
packages/core/android/src/main/java/io/sentry/react/RNSentryReactFragmentLifecycleTracer.java
Outdated
Show resolved
Hide resolved
packages/core/android/src/main/java/io/sentry/react/RNSentryReactFragmentLifecycleTracer.java
Outdated
Show resolved
Hide resolved
packages/core/android/src/main/java/io/sentry/react/RNSentryReactFragmentLifecycleTracer.java
Outdated
Show resolved
Hide resolved
packages/core/android/src/main/java/io/sentry/react/RNSentryReactFragmentLifecycleTracer.java
Outdated
Show resolved
Hide resolved
packages/core/android/src/main/java/io/sentry/react/RNSentryReactFragmentLifecycleTracer.java
Outdated
Show resolved
Hide resolved
|
Overall looks good! lets get the approval of @romtsn before merging. |
| } | ||
| } | ||
|
|
||
| private @Nullable ReplayIntegration getReplayIntegration() { |
There was a problem hiding this comment.
nit: I think you could simplify this method by calling ScopesAdapter.getInstance().getOptions().getReplayController() and then checking if it's instanceof ReplayIntegration. This would spare a few cycles.
There was a problem hiding this comment.
Alternatively, we could even expose the onWindowSizeChanged method in the ReplayController interface, but would require a new Android SDK release, so I'll leave it up to you to decide :)
There was a problem hiding this comment.
Great idea @romtsn 🙇
Updated with dbee1d4 and verified that everything works as before.
Alternatively, we could even expose the onWindowSizeChanged method in the ReplayController interface, but would require a new Android SDK release, so I'll leave it up to you to decide :)
I'll add a note to iterate on this as an improvement. It would be nice to ship the fix soon since this is also a PII.
| } | ||
| }); | ||
|
|
||
| // Add layout listener to detect configuration changes after detaching any previous one |
There was a problem hiding this comment.
so, as I understood this logic only applies when ReactNavigationIntegration is used and ttid-tracking is enabled. I was wondering if we should actually make it independent from the both above, and just introduce a replay-specific FragmentLifecycleCallbacks? Also, is this only a problem when fragments are used (i.e. reactNavigation), or also applies to activity-based apps?
There was a problem hiding this comment.
Good catch @romtsn 🙇
make it independent from the both above, and just introduce a replay-specific FragmentLifecycleCallbacks?
Makes sense 👍 Updated with bbddc27
Also, is this only a problem when fragments are used (i.e. reactNavigation), or also applies to activity-based apps?
I was able to still reproduce the issue on a bare app that seems to have only Activities. I think for most real world scenarios the fragment implementation would be used and I would opt for handling this case separately.
Example Activity only app
import React from 'react';
import { Button, View, Text, StyleSheet } from 'react-native';
import * as Sentry from '@sentry/react-native';
Sentry.init({
dsn: 'DSN',
// Adds more context data to events (IP address, cookies, user, etc.)
// For more information, visit: https://docs.sentry.io/platforms/react-native/data-management/data-collected/
sendDefaultPii: true,
// Enable Logs
enableLogs: true,
// Configure Session Replay
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1,
integrations: [Sentry.mobileReplayIntegration(), Sentry.feedbackIntegration()],
// uncomment the line below to enable Spotlight (https://spotlightjs.com)
// spotlight: __DEV__,
});
export default function App() {
return (
<View style={styles.container}>
<Text style={styles.text}>Hello, Minimal App!</Text>
<Button title='Try!' onPress={ () => { Sentry.captureException(new Error('First error')) }}/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
},
text: {
fontSize: 20,
},
});
export default Sentry.wrap(App);
There was a problem hiding this comment.
I agree, let's not care about it for now. If we get reports we can do that later 👍 Let me review the updated logic
romtsn
left a comment
There was a problem hiding this comment.
One nit comment and one question - going to approve once we figure those out. But this looks great already, thanks for investigating!
|
There seems to be a CI issue at this point irrelevant from this PR that causes failures like: |
| final RNSentryReplayFragmentLifecycleTracer fragmentLifecycleTracer = | ||
| new RNSentryReplayFragmentLifecycleTracer(logger); | ||
|
|
||
| final @Nullable FragmentActivity fragmentActivity = (FragmentActivity) getCurrentActivity(); |
There was a problem hiding this comment.
I assume it should be always a FragmentActivity but maybe worth to have an instanceof check just in case?
There was a problem hiding this comment.
I used the pattern from above but I think it's a good idea to add the check 👍 Updated with a8ad163
| private @NotNull final ILogger logger; | ||
|
|
||
| @SuppressWarnings("PMD.AvoidUsingVolatile") | ||
| private @Nullable volatile ReplayIntegration replayIntegration; |
There was a problem hiding this comment.
I assume volatile is not really needed here since everything is being accessed from the main thread (OnGlobalLayoutListener is likely called on the main thread too). But it's also fine as it is
| private int lastWidth = -1; | ||
| private int lastHeight = -1; | ||
|
|
||
| private @Nullable View currentView; |
There was a problem hiding this comment.
theoretically this shouldn't leak since we nullify it in onFragmentViewDestroyed but maybe worth to have it as a WeakRef just in case?
romtsn
left a comment
There was a problem hiding this comment.
LGTM, thanks for addressing the feedback!
📢 Type of change
📜 Description
Fixes orientation change misalignment for session replay on Android by manually triggering the onWindowSizeChanged when the screen size changes. This should cover orientation changes or other screen size changes (e.g. foldables).
💡 Motivation and Context
Fixes #4982
💚 How did you test it?
Tested with the latest SDK and the
screenshotStrategymakes a difference on how the bug is manifested📝 Checklist
sendDefaultPIIis enabled🔮 Next steps