Skip to content
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

Fix native animated event lag on Android #11994

Closed

Conversation

janicduplessis
Copy link
Contributor

Native animated events sometimes end up lagging a frame behind on android because we perform the update in the normal animation loop instead of doing it immediately when we receive the event. We had the same issue on iOS and was fixed in a similar way.

Moved some code around to have a method that updates a list of node that we can use to update the node in the animated event handler and also use it in the animation update loop.

Test plan
Tested that it did fix sticky headers lagging a frame behind during momentum scrolling in my PR #11315 and also tested the native animations examples still work properly.

@facebook-github-bot facebook-github-bot added CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. GH Review: review-needed labels Jan 19, 2017
@janicduplessis
Copy link
Contributor Author

cc @kmagiera @ide @ryangomba

@janicduplessis
Copy link
Contributor Author

The only downside of separating the update loop into an extra method is that it does add an extra fixed size array allocation but does simplify the code a bit.

@facebook-github-bot facebook-github-bot added Import Started This pull request has been imported. This does not imply the PR has been approved. and removed GH Review: review-needed labels Jan 31, 2017
@facebook-github-bot
Copy link
Contributor

@sahrens has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@sahrens
Copy link
Contributor

sahrens commented Jan 31, 2017

I patched this in along with #11315 and everything works great, but I don't have any context on the code - @kmagiera @ide @ryangomba @AaaChiuuu @achen1, can one of you guys give the go-ahead on this?

@facebook-github-bot facebook-github-bot added Import Failed and removed Import Started This pull request has been imported. This does not imply the PR has been approved. labels Feb 1, 2017
@facebook-github-bot
Copy link
Contributor

I tried to merge this pull request into the Facebook internal repo but some checks failed. To unblock yourself please check the following: Does this pull request pass all open source tests on GitHub? If not please fix those. Does the code still apply cleanly on top of GitHub master? If not can please rebase. In all other cases this means some internal test failed, for example a part of a fb app won't work with this pull request. I've added the Import Failed label to this pull request so it is easy for someone at fb to find the pull request and check what failed. If you don't see anyone comment in a few days feel free to comment mentioning one of the core contributors to the project so they get a notification.

@mkonicek
Copy link
Contributor

mkonicek commented Feb 1, 2017

Let's ship it since it works for you @sahrens. Test plan looks good.

@mkonicek
Copy link
Contributor

mkonicek commented Feb 1, 2017

@facebook-github-bot shipit

@facebook-github-bot facebook-github-bot added GH Review: accepted Import Started This pull request has been imported. This does not imply the PR has been approved. labels Feb 1, 2017
@facebook-github-bot
Copy link
Contributor

@mkonicek has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@facebook-github-bot facebook-github-bot removed the Import Started This pull request has been imported. This does not imply the PR has been approved. label Feb 2, 2017
@facebook-github-bot
Copy link
Contributor

I tried to merge this pull request into the Facebook internal repo but some checks failed. To unblock yourself please check the following: Does this pull request pass all open source tests on GitHub? If not please fix those. Does the code still apply cleanly on top of GitHub master? If not can please rebase. In all other cases this means some internal test failed, for example a part of a fb app won't work with this pull request. I've added the Import Failed label to this pull request so it is easy for someone at fb to find the pull request and check what failed. If you don't see anyone comment in a few days feel free to comment mentioning one of the core contributors to the project so they get a notification.

@mkonicek
Copy link
Contributor

mkonicek commented Feb 2, 2017

@facebook-github-bot shipit

@facebook-github-bot facebook-github-bot added the Import Started This pull request has been imported. This does not imply the PR has been approved. label Feb 2, 2017
@facebook-github-bot
Copy link
Contributor

@mkonicek has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@facebook-github-bot
Copy link
Contributor

I tried to merge this pull request into the Facebook internal repo but some checks failed. To unblock yourself please check the following: Does this pull request pass all open source tests on GitHub? If not please fix those. Does the code still apply cleanly on top of GitHub master? If not can please rebase. In all other cases this means some internal test failed, for example a part of a fb app won't work with this pull request. I've added the Import Failed label to this pull request so it is easy for someone at fb to find the pull request and check what failed. If you don't see anyone comment in a few days feel free to comment mentioning one of the core contributors to the project so they get a notification.

@facebook-github-bot facebook-github-bot removed the Import Started This pull request has been imported. This does not imply the PR has been approved. label Feb 3, 2017
@facebook-github-bot facebook-github-bot added the Import Started This pull request has been imported. This does not imply the PR has been approved. label Feb 7, 2017
@facebook-github-bot
Copy link
Contributor

@sahrens has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@sahrens
Copy link
Contributor

sahrens commented Feb 7, 2017

@janicduplessis: this is having trouble importing - maybe there is a merge conflict? Can you rebase and update?

@janicduplessis
Copy link
Contributor Author

@sahrens Weird, doesn't look like there was any conficts, updated to master.

@janicduplessis janicduplessis removed Import Failed Import Started This pull request has been imported. This does not imply the PR has been approved. labels Feb 7, 2017
@sahrens
Copy link
Contributor

sahrens commented Feb 8, 2017

There were some internal flow errors...I think I fixed them all but this is still having trouble. I'll get it through soon, I promise!

@janicduplessis
Copy link
Contributor Author

@sahrens Any updates on this?

@sahrens
Copy link
Contributor

sahrens commented Feb 13, 2017

@astreet said he would take a look - any progress, Andy?

@sahrens
Copy link
Contributor

sahrens commented Feb 13, 2017

There were some internal test failures :(

@astreet
Copy link
Contributor

astreet commented Feb 14, 2017

Hmm? I haven't seen this PR before but I can take a look now

@@ -353,9 +355,47 @@ public void onEventDispatch(Event event) {
*/
public void runUpdates(long frameTimeNanos) {
UiThreadUtil.assertOnUiThread();
boolean hasFinishedAnimations = false;

List<AnimatedNode> nodes = new ArrayList<AnimatedNode>(mUpdatedNodes.size() + mActiveAnimations.size());
Copy link
Contributor

Choose a reason for hiding this comment

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

Re-allocating a new list on every frame isn't great: you can just keep a static one around to re-use.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍


for (int i = 0; i < mActiveAnimations.size(); i++) {
AnimationDriver animation = mActiveAnimations.valueAt(i);
animation.runAnimationStep(frameTimeNanos);
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not familiar with this code, but this seems odd: why are we running the animation and then updating all the nodes for the frame below on line L376? I.e., shouldn't we be updating first?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This code didn't change, I just moved it in it's own method. How it works is we run the animation drivers first, which update the value nodes then update all the other nodes that are connected to these updated value nodes, which will cause props nodes to update their connected view.

@@ -334,7 +335,8 @@ public void onEventDispatch(Event event) {
EventAnimationDriver eventDriver = mEventDrivers.get(event.getViewTag() + eventName);
if (eventDriver != null) {
event.dispatch(eventDriver);
mUpdatedNodes.put(eventDriver.mValueNode.mTag, eventDriver.mValueNode);

updateNodes(Collections.singletonList((AnimatedNode) eventDriver.mValueNode));
Copy link
Contributor

Choose a reason for hiding this comment

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

You have an updateNodes here when you receive an event, and then in the same frame you could runUpdates and updateNodes again: is that correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There are cases where this won't happen in the same frame, like during momentum scrolling, this is why we need to update it immediatly instead of relying on runUpdates which is called on each frame by the Choreographer.

These functions only update nodes that are marked as needing an update so it won't update twice in a frame.

@sahrens
Copy link
Contributor

sahrens commented Feb 14, 2017 via email

@janicduplessis
Copy link
Contributor Author

@astreet Updated to use a static list.

@@ -51,6 +52,9 @@
*/
/*package*/ class NativeAnimatedNodesManager implements EventDispatcherListener {

// Used to avoid allocating a new array on every frame in runUpdates.
private final static List<AnimatedNode> sRunUpdateNodeList = new ArrayList<>();
Copy link
Contributor

Choose a reason for hiding this comment

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

can you make this an instance variable unless it truly needs to be static and shared across NodesManagers?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Both would work, having it as a static ivar avoids having multiple copies in case there are multiple instances of the animated module (like in exponent). The array gets cleaned up after each invocation of runUpdates and this always runs from the same thread so it is safe.

If you feel it should be a normal ivar I don't really mind, just saves a few bytes in rare situations.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, I'd prefer an ivar in case someone somewhere decides to have multiple RN instances running at the same time :)

@@ -51,6 +52,9 @@
*/
/*package*/ class NativeAnimatedNodesManager implements EventDispatcherListener {

// Used to avoid allocating a new array on every frame in runUpdates.
private final static List<AnimatedNode> sRunUpdateNodeList = new ArrayList<>();
Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, I'd prefer an ivar in case someone somewhere decides to have multiple RN instances running at the same time :)

@janicduplessis
Copy link
Contributor Author

@astreet Updated to an ivar

@facebook-github-bot facebook-github-bot added the Import Started This pull request has been imported. This does not imply the PR has been approved. label Feb 20, 2017
@facebook-github-bot
Copy link
Contributor

@astreet has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

GaborWnuk pushed a commit to GaborWnuk/react-native that referenced this pull request Feb 28, 2017
Summary:
Native animated events sometimes end up lagging a frame behind on android because we perform the update in the normal animation loop instead of doing it immediately when we receive the event. We had the same issue on iOS and was fixed in a similar way.

Moved some code around to have a method that updates a list of node that we can use to update the node in the animated event handler and also use it in the animation update loop.

**Test plan**
Tested that it did fix sticky headers lagging a frame behind during momentum scrolling in my PR facebook#11315 and also tested the native animations examples still work properly.
Closes facebook#11994

Reviewed By: mkonicek

Differential Revision: D4488977

Pulled By: sahrens

fbshipit-source-id: 831a1565bc7b8fa88cadd5a8c1be876fbdefbf66
dudeinthemirror pushed a commit to dudeinthemirror/react-native that referenced this pull request Mar 1, 2017
Summary:
Native animated events sometimes end up lagging a frame behind on android because we perform the update in the normal animation loop instead of doing it immediately when we receive the event. We had the same issue on iOS and was fixed in a similar way.

Moved some code around to have a method that updates a list of node that we can use to update the node in the animated event handler and also use it in the animation update loop.

**Test plan**
Tested that it did fix sticky headers lagging a frame behind during momentum scrolling in my PR facebook#11315 and also tested the native animations examples still work properly.
Closes facebook#11994

Reviewed By: mkonicek

Differential Revision: D4488977

Pulled By: sahrens

fbshipit-source-id: 831a1565bc7b8fa88cadd5a8c1be876fbdefbf66
dudeinthemirror pushed a commit to dudeinthemirror/react-native that referenced this pull request Mar 1, 2017
Summary:
Native animated events sometimes end up lagging a frame behind on android because we perform the update in the normal animation loop instead of doing it immediately when we receive the event. We had the same issue on iOS and was fixed in a similar way.

Moved some code around to have a method that updates a list of node that we can use to update the node in the animated event handler and also use it in the animation update loop.

**Test plan**
Tested that it did fix sticky headers lagging a frame behind during momentum scrolling in my PR facebook#11315 and also tested the native animations examples still work properly.
Closes facebook#11994

Reviewed By: mkonicek

Differential Revision: D4488977

Pulled By: sahrens

fbshipit-source-id: 831a1565bc7b8fa88cadd5a8c1be876fbdefbf66
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Import Started This pull request has been imported. This does not imply the PR has been approved.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants