-
Notifications
You must be signed in to change notification settings - Fork 24.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Separate window dimensions change from orientation #15181
Conversation
Summary: There was a bug with RN.Dimensions returning incorrect window dimensions. In certain cases when device was in portrait, window dimensions reported landscape dimensions and vice versa. This happened because in certain scenarios, after device orientation changed, dimensions update event from ReactRootView had incorrect dimensions. Was able to reproduce this when device was rotated during app launch. After rotation global layout listener callback gets invoked. Inside the callback current and previous orientations are compared. When a change is detected, orientation and dimension change events are sent to JS. It is assumed, when orientation changes, new dimensions are available immediately. This is not the case for window dimensions as they are retrieved from resources object which gets updated asynchronously after orientation change. In cases when app is doing a lot of work on the main thread, like app startup, it takes more time to update the resources object. And when orientation change is detected in global layout, resources object is not updated with new dimensions yet. This causes dimensions update to be sent to JS with old window dimensions. Global layout listener callback does get invoked a second time when resources object is finally updated with new dimensions, but since orientation no longer changes, no event is sent to JS. Fixed this by separating dimensions update from orientation update. Now RN keeps track of previous window and screen dimension values. When a change is detected, an event is sent to JS with updated dimensions. This ensures that whenever dimensions change, JS gets the updated values. This has a side effect of sending dimension update twice in some cases. One example is the case above where window dimensions take time to update, but screen dimensions are updated immediately. This will cause two events to be sent to JS. One for window dimensions and one for screen dimensions update. Other change is that initial value for both window and screen fields is empty. Which results in first change to trigger an event. Previously initial orientation value was 0 which meant when app started in normal portrait orientation, first layout did not trigger a dimension update event. Now even first layout sends the event. This should not be an issue as it is to make sure dimensions in JS side are correct. Testing: Verified with a sample app that correct dimensions are available when app launches. Verified that after orientation dimensions are updated. Verified that in the scenario described above where window dimensions are updated later, we get correct dimension values in JS. We have incorporated this fix into our app and have been testing it internally. Ats Jenk Microsoft Corp.
DisplayMetricsHolder.initDisplayMetrics(getContext()); | ||
// Check changes to both window and screen display metrics since they may not update at the same time. | ||
if (!areMetricsEqual(mWindowMetrics, DisplayMetricsHolder.getWindowDisplayMetrics()) | ||
|| !areMetricsEqual(mScreenMetrics, DisplayMetricsHolder.getScreenDisplayMetrics())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make the code style consistent with existing code.
// DisplayMetrics didn't have an equals method before API 17. | ||
// Check all public fields manually. | ||
return displayMetrics.widthPixels == otherMetrics.widthPixels | ||
&& displayMetrics.heightPixels == otherMetrics.heightPixels |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant we have to put &&
on previous line (seems that is current style). (Sorry.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. Yes that seems to be the pattern. I'll fix that.
Indentation for the if condition did not match the current style either as per line 147.
&& and || should be on the same line when code splits to next line.
@atsjenk Great! Thank you! |
@shergin any updates on this PR? |
@shergin has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator. |
Summary: **Summary:** There was a bug with RN.Dimensions returning incorrect window dimensions. In certain cases when device was in portrait, window dimensions reported landscape dimensions and vice versa. This happened because in certain scenarios, after device orientation changed, dimensions update event from ReactRootView had incorrect dimensions. Was able to reproduce this when device was rotated during app launch. After rotation global layout listener callback gets invoked. Inside the callback current and previous orientations are compared. When a change is detected, orientation and dimension change events are sent to JS. It is assumed, when orientation changes, new dimensions are available immediately. This is not the case for window dimensions as they are retrieved from resources object which gets updated asynchronously after orientation change. In cases when app is doing a lot of work on the main thread, like app startup, it takes more time to update the resources object. And when orientation change is detected in global layout, resources object is not updated with new dimensions yet. This causes dimensions update to be sent to JS with old window dimensions. Global layout listener callback does get invoked a second time when resources object is finally updated with new dimensions, but since orientation no longer changes, no event is sent to JS. Fixed this by separating dimensions update from orientation update. Now RN keeps track of previous window and screen dimension values. When a change is detected, an event is sent to JS with updated dimensions. This ensures that whenever dimensions change, JS gets the updated values. This has a side effect of sending dimension update twice in some cases. One example is the case above where window dimensions take time to update, but screen dimensions are updated immediately. This will cause two events to be sent to JS. One for window dimensions and one for screen dimensions update. Other change is that initial value for both window and screen fields is empty. Which results in first change to trigger an event. Previously initial orientation value was 0 which meant when app started in normal portrait orientation, first layout did not trigger a dimension update event. Now even first layout sends the event. This should not be an issue as it is to make sure dimensions in JS side are correct. **Testing:** Verified with a sample app that correct dimensions are available when app launches. Verified that after orientation dimensions are updated. Verified that in the scenario described above where window dimensions are updated later, we get correct dimension values in JS. We have incorporated this fix into our app and have been testing it internally. Ats Jenk Microsoft Corp. <!-- Thank you for sending the PR! If you changed any code, please provide us with clear instructions on how you verified your changes work. In other words, a test plan is *required*. Bonus points for screenshots and videos! Please read the Contribution Guidelines at https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md to learn more about contributing to React Native. Happy contributing! --> Closes #15181 Differential Revision: D5552195 Pulled By: shergin fbshipit-source-id: d1f190cb960090468886ff56cda58cac296745db
Summary:
There was a bug with RN.Dimensions returning incorrect window dimensions. In certain cases when device was in portrait, window dimensions reported landscape dimensions and vice versa.
This happened because in certain scenarios, after device orientation changed, dimensions update event from ReactRootView had incorrect dimensions.
Was able to reproduce this when device was rotated during app launch. After rotation global layout listener callback gets invoked. Inside the callback current and previous orientations are compared. When a change is detected, orientation and dimension change events are sent to JS. It is assumed, when orientation changes, new dimensions are available immediately. This is not the case for window dimensions as they are retrieved from resources object which gets updated asynchronously after orientation change. In cases when app is doing a lot of work on the main thread, like app startup, it takes more time to update the resources object. And when orientation change is detected in global layout, resources object is not updated with new dimensions yet. This causes dimensions update to be sent to JS with old window dimensions.
Global layout listener callback does get invoked a second time when resources object is finally updated with new dimensions, but since orientation no longer changes, no event is sent to JS.
Fixed this by separating dimensions update from orientation update. Now RN keeps track of previous window and screen dimension values. When a change is detected, an event is sent to JS with updated dimensions. This ensures that whenever dimensions change, JS gets the updated values.
This has a side effect of sending dimension update twice in some cases.
One example is the case above where window dimensions take time to update, but screen dimensions are updated immediately. This will cause two events to be sent to JS. One for window dimensions and one for screen dimensions update.
Other change is that initial value for both window and screen fields is empty. Which results in first change to trigger an event. Previously initial orientation value was 0 which meant when app started in normal portrait orientation, first layout did not trigger a dimension update event. Now even first layout sends the event. This should not be an issue as it is to make sure dimensions in JS side are correct.
Testing:
Verified with a sample app that correct dimensions are available when app launches.
Verified that after orientation dimensions are updated.
Verified that in the scenario described above where window dimensions are updated later, we get correct dimension values in JS.
We have incorporated this fix into our app and have been testing it internally.
Ats Jenk
Microsoft Corp.