From 483f84e881204f7583ddeb4badc145b4f67f43f2 Mon Sep 17 00:00:00 2001 From: Joshua Gross Date: Wed, 20 May 2020 19:40:00 -0700 Subject: [PATCH] Android-specific LayoutAnimation integration Summary: Turn on Fabric LayoutAnimations on Android. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21675809 fbshipit-source-id: 49fbd3094532c5b486ea12a58898b986964ddd6e --- .../com/facebook/react/fabric/Binding.java | 2 + .../react/fabric/FabricUIManager.java | 25 ++++++++- .../java/com/facebook/react/fabric/jni/BUCK | 1 + .../com/facebook/react/fabric/jni/Binding.cpp | 55 +++++++++++++++++-- .../com/facebook/react/fabric/jni/Binding.h | 28 +++++++--- 5 files changed, 96 insertions(+), 15 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/Binding.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/Binding.java index 81ab4e76829..d0abc11e6dc 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/Binding.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/Binding.java @@ -70,6 +70,8 @@ public class Binding { boolean isRTL, boolean doLeftAndRightSwapInRTL); + public native void driveCxxAnimations(); + public void register( @NonNull JavaScriptContextHolder jsContext, @NonNull FabricUIManager fabricUIManager, diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index d628acaa839..8fa0963db2a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -142,6 +142,8 @@ public class FabricUIManager implements UIManager, LifecycleEventListener { */ private volatile boolean mDestroyed = false; + private boolean mDriveCxxAnimations = false; + private long mRunStartTime = 0l; private long mBatchedExecutionTime = 0l; private long mDispatchViewUpdatesTime = 0l; @@ -967,6 +969,20 @@ public class FabricUIManager implements UIManager, LifecycleEventListener { // TODO T31905686: Remove this method and add support for multi-threading performance counters } + // Called from Binding.cpp + @DoNotStrip + @AnyThread + public void onAnimationStarted() { + mDriveCxxAnimations = true; + } + + // Called from Binding.cpp + @DoNotStrip + @AnyThread + public void onAllAnimationsComplete() { + mDriveCxxAnimations = false; + } + @Override public Map getPerformanceCounters() { HashMap performanceCounters = new HashMap<>(); @@ -1002,11 +1018,18 @@ public class FabricUIManager implements UIManager, LifecycleEventListener { return; } + // Drive any animations from C++. + // There is a race condition here between getting/setting + // `mDriveCxxAnimations` which shouldn't matter; it's safe to call + // the mBinding method, unless mBinding has gone away. + if (mDriveCxxAnimations && mBinding != null) { + mBinding.driveCxxAnimations(); + } + try { dispatchPreMountItems(frameTimeNanos); tryDispatchMountItems(); - } catch (Exception ex) { FLog.e(TAG, "Exception thrown when executing UIFrameGuarded", ex); stop(); diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/BUCK b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/BUCK index 5143028cdc1..7fc222b4702 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/BUCK @@ -29,6 +29,7 @@ rn_xplat_cxx_library( deps = [ react_native_xplat_target("better:better"), react_native_xplat_target("config:config"), + react_native_xplat_target("fabric/animations:animations"), react_native_xplat_target("fabric/uimanager:uimanager"), react_native_xplat_target("fabric/scheduler:scheduler"), react_native_xplat_target("fabric/componentregistry:componentregistry"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp index 54830fe1ac3..025d035502e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -72,6 +73,10 @@ std::shared_ptr Binding::getScheduler() { return scheduler_; } +LayoutAnimationDriver *Binding::getAnimationDriver() { + return (animationDriver_ ? animationDriver_.get() : nullptr); +} + void Binding::startSurface( jint surfaceId, jni::alias_ref moduleName, @@ -91,7 +96,8 @@ void Binding::startSurface( moduleName->toStdString(), initialProps->consume(), {}, - context); + context, + getAnimationDriver()); } void Binding::startSurfaceWithConstraints( @@ -137,7 +143,8 @@ void Binding::startSurfaceWithConstraints( moduleName->toStdString(), initialProps->consume(), constraints, - context); + context, + getAnimationDriver()); } void Binding::renderTemplateToSurface(jint surfaceId, jstring uiTemplate) { @@ -276,19 +283,23 @@ void Binding::installFabricUIManager( collapseDeleteCreateMountingInstructions_ = reactNativeConfig_->getBool( "react_fabric:enabled_collapse_delete_create_mounting_instructions"); - disableVirtualNodePreallocation_ = reactNativeConfig_->getBool( - "react_fabric:disable_virtual_node_preallocation"); - disablePreallocateViews_ = reactNativeConfig_->getBool( "react_fabric:disabled_view_preallocation_android"); + bool enableLayoutAnimations_ = reactNativeConfig_->getBool( + "react_fabric:enabled_layout_animations_android"); + auto toolbox = SchedulerToolbox{}; toolbox.contextContainer = contextContainer; toolbox.componentRegistryFactory = componentsRegistry->buildRegistryFunction; toolbox.runtimeExecutor = runtimeExecutor; toolbox.synchronousEventBeatFactory = synchronousBeatFactory; toolbox.asynchronousEventBeatFactory = asynchronousBeatFactory; - scheduler_ = std::make_shared(toolbox, nullptr, this); + + if (enableLayoutAnimations_) { + animationDriver_ = std::make_unique(this); + } + scheduler_ = std::make_shared(toolbox, getAnimationDriver(), this); } void Binding::uninstallFabricUIManager() { @@ -870,6 +881,37 @@ void Binding::setPixelDensity(float pointScaleFactor) { pointScaleFactor_ = pointScaleFactor; } +void Binding::onAnimationStarted() { + jni::global_ref localJavaUIManager = getJavaUIManager(); + if (!localJavaUIManager) { + LOG(ERROR) << "Binding::animationsStarted: JavaUIManager disappeared"; + return; + } + + static auto layoutAnimationsStartedJNI = + jni::findClassStatic(UIManagerJavaDescriptor) + ->getMethod("onAnimationStarted"); + + layoutAnimationsStartedJNI(localJavaUIManager); +} +void Binding::onAllAnimationsComplete() { + jni::global_ref localJavaUIManager = getJavaUIManager(); + if (!localJavaUIManager) { + LOG(ERROR) << "Binding::allAnimationsComplete: JavaUIManager disappeared"; + return; + } + + static auto allAnimationsCompleteJNI = + jni::findClassStatic(UIManagerJavaDescriptor) + ->getMethod("onAllAnimationsComplete"); + + allAnimationsCompleteJNI(localJavaUIManager); +} + +void Binding::driveCxxAnimations() { + scheduler_->animationTick(); +} + void Binding::schedulerDidRequestPreliminaryViewAllocation( const SurfaceId surfaceId, const ShadowView &shadowView) { @@ -994,6 +1036,7 @@ void Binding::registerNatives() { makeNativeMethod("stopSurface", Binding::stopSurface), makeNativeMethod("setConstraints", Binding::setConstraints), makeNativeMethod("setPixelDensity", Binding::setPixelDensity), + makeNativeMethod("driveCxxAnimations", Binding::driveCxxAnimations), makeNativeMethod( "uninstallFabricUIManager", Binding::uninstallFabricUIManager)}); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.h b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.h index c5c216c39ab..504fc83b252 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.h +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.h @@ -8,10 +8,12 @@ #pragma once #include +#include #include #include #include #include +#include #include #include #include "ComponentFactoryDelegate.h" @@ -22,7 +24,9 @@ namespace react { class Instance; -class Binding : public jni::HybridClass, public SchedulerDelegate { +class Binding : public jni::HybridClass, + public SchedulerDelegate, + public LayoutAnimationStatusDelegate { public: constexpr static const char *const kJavaDescriptor = "Lcom/facebook/react/fabric/Binding;"; @@ -73,26 +77,28 @@ class Binding : public jni::HybridClass, public SchedulerDelegate { void stopSurface(jint surfaceId); void schedulerDidFinishTransaction( - MountingCoordinator::Shared const &mountingCoordinator); + MountingCoordinator::Shared const &mountingCoordinator) override; void schedulerDidRequestPreliminaryViewAllocation( const SurfaceId surfaceId, - const ShadowView &shadowView); + const ShadowView &shadowView) override; void schedulerDidDispatchCommand( const ShadowView &shadowView, std::string const &commandName, - folly::dynamic const args); - - void setPixelDensity(float pointScaleFactor); + folly::dynamic const args) override; void schedulerDidSetJSResponder( SurfaceId surfaceId, const ShadowView &shadowView, const ShadowView &initialShadowView, - bool blockNativeResponder); + bool blockNativeResponder) override; - void schedulerDidClearJSResponder(); + void schedulerDidClearJSResponder() override; + + void setPixelDensity(float pointScaleFactor); + + void driveCxxAnimations(); void uninstallFabricUIManager(); @@ -100,6 +106,12 @@ class Binding : public jni::HybridClass, public SchedulerDelegate { jni::global_ref javaUIManager_; std::mutex javaUIManagerMutex_; + // LayoutAnimations + virtual void onAnimationStarted() override; + virtual void onAllAnimationsComplete() override; + LayoutAnimationDriver *getAnimationDriver(); + std::unique_ptr animationDriver_; + std::shared_ptr scheduler_; std::mutex schedulerMutex_;