From 1f384f75da74e53e973ed8b1121c0a79b9468ab2 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Wed, 5 Apr 2023 07:09:33 -0700 Subject: [PATCH] Add fabric support for maintainVisibleContentPosition on Android (#35994) Summary: Implement a few missing bits for `maintainVisibleContentPosition` to work with fabric. The main thing needed is to add 2 fabric renderer listener methods to allow to hook into specific parts of the rendering process. We need some code to execute before view updates are executed and after view updates are executed. The current methods that are exposed do not work for this case. `willDispatchViewUpdates` is called from JS thread, and there doesn't seem to be a way to add UI blocks that will be executed at the right time like we do in paper. `didDispatchMountItems` is called for every frame which we don't want and will cause lots of overhead. After that we simply need to call the right methods in the new renderer listener methods. ## Changelog Pull Request resolved: https://github.com/facebook/react-native/pull/35994 Test Plan: Tested in RN tester maintainVisibleContentPosition example on Android with fabric enabled. Reviewed By: cipolleschi Differential Revision: D44131763 Pulled By: cortinico fbshipit-source-id: 32c0b5867d460537b18a70d472fd58052da6cf80 --- .../react/animated/NativeAnimatedModule.java | 10 +++++++ .../react/bridge/UIManagerListener.java | 27 +++++++++++++++++-- .../react/fabric/FabricUIManager.java | 14 ++++++++++ .../fabric/mounting/MountItemDispatcher.java | 9 +++++++ .../MaintainVisibleScrollPositionHelper.java | 19 +++++++++++++ 5 files changed, 77 insertions(+), 2 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java index ce7520ee9bd..76749aaff7f 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java @@ -295,6 +295,16 @@ public class NativeAnimatedModule extends NativeAnimatedModuleSpec mCurrentFrameNumber++; } + @Override + public void willMountItems(UIManager uiManager) { + // noop + } + + @Override + public void didMountItems(UIManager uiManager) { + // noop + } + // For FabricUIManager only @Override @UiThread diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManagerListener.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManagerListener.java index 984bcf15848..505999b3d5a 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManagerListener.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManagerListener.java @@ -12,10 +12,33 @@ public interface UIManagerListener { /** * Called right before view updates are dispatched at the end of a batch. This is useful if a * module needs to add UIBlocks to the queue before it is flushed. + * + *

This is called by Paper only. */ void willDispatchViewUpdates(UIManager uiManager); - /* Called right after view updates are dispatched for a frame. */ + /** + * Called on UIThread right before view updates are executed. + * + *

This is called by Fabric only. + */ + void willMountItems(UIManager uiManager); + /** + * Called on UIThread right after view updates are executed. + * + *

This is called by Fabric only. + */ + void didMountItems(UIManager uiManager); + /** + * Called on UIThread right after view updates are dispatched for a frame. Note that this will be + * called for every frame even if there are no updates. + * + *

This is called by Fabric only. + */ void didDispatchMountItems(UIManager uiManager); - /* Called right after scheduleMountItems is called in Fabric, after a new tree is committed. */ + /** + * Called right after scheduleMountItems is called in Fabric, after a new tree is committed. + * + *

This is called by Fabric only. + */ void didScheduleMountItems(UIManager uiManager); } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index 0da8e55dd8d..8e50e91a64d 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -1171,6 +1171,20 @@ public class FabricUIManager implements UIManager, LifecycleEventListener { } private class MountItemDispatchListener implements MountItemDispatcher.ItemDispatchListener { + @Override + public void willMountItems() { + for (UIManagerListener listener : mListeners) { + listener.willMountItems(FabricUIManager.this); + } + } + + @Override + public void didMountItems() { + for (UIManagerListener listener : mListeners) { + listener.didMountItems(FabricUIManager.this); + } + } + @Override public void didDispatchMountItems() { for (UIManagerListener listener : mListeners) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java index 36abe8dd9f1..6861961c472 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java @@ -192,6 +192,8 @@ public class MountItemDispatcher { return false; } + mItemDispatchListener.willMountItems(); + // As an optimization, execute all ViewCommands first // This should be: // 1) Performant: ViewCommands are often a replacement for SetNativeProps, which we've always @@ -298,6 +300,9 @@ public class MountItemDispatcher { } mBatchedExecutionTime += SystemClock.uptimeMillis() - batchedExecutionStartTime; } + + mItemDispatchListener.didMountItems(); + Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); return true; @@ -414,6 +419,10 @@ public class MountItemDispatcher { } public interface ItemDispatchListener { + void willMountItems(); + + void didMountItems(); + void didDispatchMountItems(); } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/MaintainVisibleScrollPositionHelper.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/MaintainVisibleScrollPositionHelper.java index 6ce4d8b23d2..f76c432b590 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/MaintainVisibleScrollPositionHelper.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/MaintainVisibleScrollPositionHelper.java @@ -18,6 +18,7 @@ import com.facebook.react.bridge.UIManager; import com.facebook.react.bridge.UIManagerListener; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.uimanager.UIManagerHelper; +import com.facebook.react.uimanager.common.UIManagerType; import com.facebook.react.uimanager.common.ViewUtil; import com.facebook.react.views.scroll.ReactScrollViewHelper.HasSmoothScroll; import com.facebook.react.views.view.ReactViewGroup; @@ -89,6 +90,14 @@ public class MaintainVisibleScrollPositionHelper