From 8dc5ca6a6a4cacdbff0169739017a7c599b55637 Mon Sep 17 00:00:00 2001 From: Andrei Shikov Date: Mon, 8 Mar 2021 12:17:38 -0800 Subject: [PATCH] Use `SurfaceHandler` in ReactSurface Summary: Changelog: [Internal] Updates `ReactSurface` to use `SurfaceHandler` internally. This removes most of the internal state in `ReactSurface` and propagates all the calls to the `SurfaceHandler`. `FabricUIManager` now uses `SurfaceHandler` to start/stop the surface. SurfaceId is still used for view operations. SurfaceId is also now mutable to play better with existing Android infra. Reviewed By: shergin, mdvacca Differential Revision: D26112992 fbshipit-source-id: 52e6860084d739381317035dc3011956d452063c --- .../com/facebook/react/fabric/Binding.java | 4 + .../react/fabric/FabricUIManager.java | 33 +++++ .../facebook/react/fabric/SurfaceHandler.java | 47 +++++++ .../react/fabric/SurfaceHandlerBinding.java | 113 ++++++++++++++++ .../com/facebook/react/fabric/jni/Binding.cpp | 41 +++--- .../com/facebook/react/fabric/jni/Binding.h | 5 + .../com/facebook/react/fabric/jni/OnLoad.cpp | 2 + .../fabric/jni/SurfaceHandlerBinding.cpp | 123 ++++++++++++++++++ .../react/fabric/jni/SurfaceHandlerBinding.h | 64 +++++++++ .../renderer/scheduler/SurfaceHandler.cpp | 5 + .../react/renderer/scheduler/SurfaceHandler.h | 1 + 11 files changed, 423 insertions(+), 15 deletions(-) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/fabric/SurfaceHandler.java create mode 100644 ReactAndroid/src/main/java/com/facebook/react/fabric/SurfaceHandlerBinding.java create mode 100644 ReactAndroid/src/main/java/com/facebook/react/fabric/jni/SurfaceHandlerBinding.cpp create mode 100644 ReactAndroid/src/main/java/com/facebook/react/fabric/jni/SurfaceHandlerBinding.h 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 9020ab17e77..b7af81b34de 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/Binding.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/Binding.java @@ -100,4 +100,8 @@ public class Binding { public void unregister() { uninstallFabricUIManager(); } + + public native void registerSurface(SurfaceHandlerBinding surfaceHandler); + + public native void unregisterSurface(SurfaceHandlerBinding surfaceHandler); } 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 e05d0d8c830..8f5f0cbb386 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -269,6 +269,39 @@ public class FabricUIManager implements UIManager, LifecycleEventListener { return rootTag; } + public void startSurface(final View rootView, SurfaceHandler surfaceHandler) { + final int rootTag = ReactRootViewTagGenerator.getNextRootViewTag(); + + Context context = rootView.getContext(); + ThemedReactContext reactContext = + new ThemedReactContext( + mReactApplicationContext, context, surfaceHandler.getModuleName(), rootTag); + mMountingManager.startSurface(rootTag, rootView, reactContext); + + surfaceHandler.setSurfaceId(rootTag); + if (surfaceHandler instanceof SurfaceHandlerBinding) { + mBinding.registerSurface((SurfaceHandlerBinding) surfaceHandler); + } + surfaceHandler.start(); + } + + public void stopSurface(SurfaceHandler surfaceHandler) { + if (!surfaceHandler.isRunning()) { + ReactSoftException.logSoftException( + FabricUIManager.TAG, + new IllegalStateException("Trying to stop surface that hasn't started yet")); + return; + } + + mMountingManager.stopSurface(surfaceHandler.getSurfaceId()); + + surfaceHandler.stop(); + + if (surfaceHandler instanceof SurfaceHandlerBinding) { + mBinding.unregisterSurface((SurfaceHandlerBinding) surfaceHandler); + } + } + /** Method called when an event has been dispatched on the C++ side. */ @DoNotStrip @SuppressWarnings("unused") diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/SurfaceHandler.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/SurfaceHandler.java new file mode 100644 index 00000000000..2118fada6bf --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/SurfaceHandler.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.fabric; + +import com.facebook.react.bridge.NativeMap; +import javax.annotation.concurrent.ThreadSafe; + +/** Represents a Java variant of the surface, its status and inner data required to display it. */ +@ThreadSafe +public interface SurfaceHandler { + + /** Starts the surface if the surface is not running */ + void start(); + + /** Stops the surface if it is currently running */ + void stop(); + + void setProps(NativeMap props); + + /** + * Provides current surface id. Id should be updated after each call to {@link + * SurfaceHandler#stop} + */ + int getSurfaceId(); + + /** + * Updates current surface id. Id should be updated after each call to {@link SurfaceHandler#stop} + */ + void setSurfaceId(int surfaceId); + + boolean isRunning(); + + String getModuleName(); + + void setLayoutConstraints( + int widthMeasureSpec, + int heightMeasureSpec, + int offsetX, + int offsetY, + boolean doLeftAndRightSwapInRTL, + boolean isRTL); +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/SurfaceHandlerBinding.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/SurfaceHandlerBinding.java new file mode 100644 index 00000000000..fe03c3e6a19 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/SurfaceHandlerBinding.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.fabric; + +import static com.facebook.react.fabric.mounting.LayoutMetricsConversions.getMaxSize; +import static com.facebook.react.fabric.mounting.LayoutMetricsConversions.getMinSize; + +import com.facebook.jni.HybridData; +import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.react.bridge.NativeMap; + +public class SurfaceHandlerBinding implements SurfaceHandler { + static { + FabricSoLoader.staticInit(); + } + + private static final int NO_SURFACE_ID = 0; + + @DoNotStrip private final HybridData mHybridData; + private final float mPixelDensity; + + private static native HybridData initHybrid(int surfaceId, String moduleName); + + public SurfaceHandlerBinding(String moduleName, float pixelDensity) { + mHybridData = initHybrid(NO_SURFACE_ID, moduleName); + mPixelDensity = pixelDensity; + } + + @Override + public int getSurfaceId() { + return getSurfaceIdNative(); + } + + private native int getSurfaceIdNative(); + + @Override + public void setSurfaceId(int surfaceId) { + setSurfaceIdNative(surfaceId); + } + + private native void setSurfaceIdNative(int surfaceId); + + @Override + public String getModuleName() { + return getModuleNameNative(); + } + + private native String getModuleNameNative(); + + @Override + public void start() { + startNative(); + } + + private native void startNative(); + + @Override + public void stop() { + stopNative(); + } + + private native void stopNative(); + + @Override + public boolean isRunning() { + return isRunningNative(); + } + + private native boolean isRunningNative(); + + @Override + public void setLayoutConstraints( + int widthMeasureSpec, + int heightMeasureSpec, + int offsetX, + int offsetY, + boolean doLeftAndRightSwapInRTL, + boolean isRTL) { + setLayoutConstraintsNative( + getMinSize(widthMeasureSpec) / mPixelDensity, + getMaxSize(widthMeasureSpec) / mPixelDensity, + getMinSize(heightMeasureSpec) / mPixelDensity, + getMaxSize(heightMeasureSpec) / mPixelDensity, + offsetX / mPixelDensity, + offsetY / mPixelDensity, + doLeftAndRightSwapInRTL, + isRTL, + mPixelDensity); + } + + private native void setLayoutConstraintsNative( + float minWidth, + float maxWidth, + float minHeight, + float maxHeight, + float offsetX, + float offsetY, + boolean doLeftAndRightSwapInRTL, + boolean isRTL, + float pixelDensity); + + @Override + public void setProps(NativeMap props) { + setPropsNative(props); + } + + private native void setPropsNative(NativeMap props); +} 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 afa2ede1e09..1e4a912384f 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 @@ -376,6 +376,14 @@ void Binding::stopSurface(jint surfaceId) { } } +void Binding::registerSurface(SurfaceHandlerBinding *surfaceHandler) { + surfaceHandler->registerScheduler(getScheduler()); +} + +void Binding::unregisterSurface(SurfaceHandlerBinding *surfaceHandler) { + surfaceHandler->unregisterScheduler(getScheduler()); +} + static inline float scale(Float value, Float pointScaleFactor) { std::feclearexcept(FE_ALL_EXCEPT); float result = value * pointScaleFactor; @@ -1214,21 +1222,24 @@ void Binding::schedulerDidSetIsJSResponder( } void Binding::registerNatives() { - registerHybrid( - {makeNativeMethod("initHybrid", Binding::initHybrid), - makeNativeMethod( - "installFabricUIManager", Binding::installFabricUIManager), - makeNativeMethod("startSurface", Binding::startSurface), - makeNativeMethod( - "startSurfaceWithConstraints", Binding::startSurfaceWithConstraints), - makeNativeMethod( - "renderTemplateToSurface", Binding::renderTemplateToSurface), - makeNativeMethod("stopSurface", Binding::stopSurface), - makeNativeMethod("setConstraints", Binding::setConstraints), - makeNativeMethod("setPixelDensity", Binding::setPixelDensity), - makeNativeMethod("driveCxxAnimations", Binding::driveCxxAnimations), - makeNativeMethod( - "uninstallFabricUIManager", Binding::uninstallFabricUIManager)}); + registerHybrid({ + makeNativeMethod("initHybrid", Binding::initHybrid), + makeNativeMethod( + "installFabricUIManager", Binding::installFabricUIManager), + makeNativeMethod("startSurface", Binding::startSurface), + makeNativeMethod( + "startSurfaceWithConstraints", Binding::startSurfaceWithConstraints), + makeNativeMethod( + "renderTemplateToSurface", Binding::renderTemplateToSurface), + makeNativeMethod("stopSurface", Binding::stopSurface), + makeNativeMethod("setConstraints", Binding::setConstraints), + makeNativeMethod("setPixelDensity", Binding::setPixelDensity), + makeNativeMethod("driveCxxAnimations", Binding::driveCxxAnimations), + makeNativeMethod( + "uninstallFabricUIManager", Binding::uninstallFabricUIManager), + makeNativeMethod("registerSurface", Binding::registerSurface), + makeNativeMethod("unregisterSurface", Binding::unregisterSurface), + }); } } // namespace react 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 f0216d0cdee..92015e5bcd6 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 @@ -20,6 +20,7 @@ #include "ComponentFactory.h" #include "EventBeatManager.h" #include "JBackgroundExecutor.h" +#include "SurfaceHandlerBinding.h" namespace facebook { namespace react { @@ -124,6 +125,10 @@ class Binding : public jni::HybridClass, void stopSurface(jint surfaceId); + void registerSurface(SurfaceHandlerBinding *surfaceHandler); + + void unregisterSurface(SurfaceHandlerBinding *surfaceHandler); + void schedulerDidFinishTransaction( MountingCoordinator::Shared const &mountingCoordinator) override; diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/OnLoad.cpp b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/OnLoad.cpp index 03005372a65..61fcec3d1c9 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/OnLoad.cpp +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/OnLoad.cpp @@ -13,6 +13,7 @@ #include "EventBeatManager.h" #include "EventEmitterWrapper.h" #include "StateWrapperImpl.h" +#include "SurfaceHandlerBinding.h" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { return facebook::jni::initialize(vm, [] { @@ -22,5 +23,6 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { facebook::react::StateWrapperImpl::registerNatives(); facebook::react::ComponentFactory::registerNatives(); facebook::react::CoreComponentsRegistry::registerNatives(); + facebook::react::SurfaceHandlerBinding::registerNatives(); }); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/SurfaceHandlerBinding.cpp b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/SurfaceHandlerBinding.cpp new file mode 100644 index 00000000000..c8f9e31a65f --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/SurfaceHandlerBinding.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "SurfaceHandlerBinding.h" +#include + +namespace facebook { +namespace react { + +SurfaceHandlerBinding::SurfaceHandlerBinding( + SurfaceId surfaceId, + std::string const &moduleName) + : surfaceHandler_(moduleName, surfaceId) {} + +void SurfaceHandlerBinding::start() { + std::unique_lock lock(lifecycleMutex_); + + surfaceHandler_.setDisplayMode(SurfaceHandler::DisplayMode::Visible); + if (surfaceHandler_.getStatus() != SurfaceHandler::Status::Running) { + surfaceHandler_.start(); + } +} + +void SurfaceHandlerBinding::stop() { + std::unique_lock lock(lifecycleMutex_); + + if (surfaceHandler_.getStatus() == SurfaceHandler::Status::Running) { + surfaceHandler_.stop(); + } +} + +jint SurfaceHandlerBinding::getSurfaceId() { + return surfaceHandler_.getSurfaceId(); +} + +void SurfaceHandlerBinding::setSurfaceId(jint surfaceId) { + surfaceHandler_.setSurfaceId(surfaceId); +} + +jboolean SurfaceHandlerBinding::isRunning() { + return surfaceHandler_.getStatus() == SurfaceHandler::Status::Running; +} + +jni::local_ref SurfaceHandlerBinding::getModuleName() { + return jni::make_jstring(surfaceHandler_.getModuleName()); +} + +jni::local_ref +SurfaceHandlerBinding::initHybrid( + jni::alias_ref, + jint surfaceId, + jni::alias_ref moduleName) { + auto env = jni::Environment::current(); + const char *moduleNameValue = + env->GetStringUTFChars(moduleName.get(), JNI_FALSE); + env->ReleaseStringUTFChars(moduleName.get(), moduleNameValue); + + return makeCxxInstance(surfaceId, moduleNameValue); +} + +void SurfaceHandlerBinding::registerScheduler( + std::shared_ptr scheduler) { + scheduler->registerSurface(surfaceHandler_); +} + +void SurfaceHandlerBinding::unregisterScheduler( + std::shared_ptr scheduler) { + scheduler->unregisterSurface(surfaceHandler_); +} + +void SurfaceHandlerBinding::setLayoutConstraints( + jfloat minWidth, + jfloat maxWidth, + jfloat minHeight, + jfloat maxHeight, + jfloat offsetX, + jfloat offsetY, + jboolean doLeftAndRightSwapInRTL, + jboolean isRTL, + jfloat pixelDensity) { + LayoutConstraints constraints = {}; + constraints.minimumSize = {minWidth, minHeight}; + constraints.maximumSize = {maxWidth, maxHeight}; + constraints.layoutDirection = + isRTL ? LayoutDirection::RightToLeft : LayoutDirection::LeftToRight; + + LayoutContext context = {}; + context.swapLeftAndRightInRTL = doLeftAndRightSwapInRTL; + context.pointScaleFactor = pixelDensity; + context.viewportOffset = {offsetX, offsetY}; + + surfaceHandler_.constraintLayout(constraints, context); +} + +void SurfaceHandlerBinding::setProps(NativeMap *props) { + surfaceHandler_.setProps(props->consume()); +} + +void SurfaceHandlerBinding::registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", SurfaceHandlerBinding::initHybrid), + makeNativeMethod( + "getSurfaceIdNative", SurfaceHandlerBinding::getSurfaceId), + makeNativeMethod( + "setSurfaceIdNative", SurfaceHandlerBinding::setSurfaceId), + makeNativeMethod("isRunningNative", SurfaceHandlerBinding::isRunning), + makeNativeMethod( + "getModuleNameNative", SurfaceHandlerBinding::getModuleName), + makeNativeMethod("startNative", SurfaceHandlerBinding::start), + makeNativeMethod("stopNative", SurfaceHandlerBinding::stop), + makeNativeMethod( + "setLayoutConstraintsNative", + SurfaceHandlerBinding::setLayoutConstraints), + makeNativeMethod("setPropsNative", SurfaceHandlerBinding::setProps), + }); +} + +} // namespace react +} // namespace facebook diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/SurfaceHandlerBinding.h b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/SurfaceHandlerBinding.h new file mode 100644 index 00000000000..514fc11f529 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/SurfaceHandlerBinding.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +namespace facebook { +namespace react { + +class SurfaceHandlerBinding : public jni::HybridClass { + public: + constexpr static const char *const kJavaDescriptor = + "Lcom/facebook/react/fabric/SurfaceHandlerBinding;"; + + static void registerNatives(); + + SurfaceHandlerBinding(SurfaceId surfaceId, std::string const &moduleName); + + void start(); + void stop(); + + void registerScheduler(std::shared_ptr scheduler); + void unregisterScheduler(std::shared_ptr scheduler); + + jint getSurfaceId(); + void setSurfaceId(jint surfaceId); + jni::local_ref getModuleName(); + + jboolean isRunning(); + + void setLayoutConstraints( + jfloat minWidth, + jfloat maxWidth, + jfloat minHeight, + jfloat maxHeight, + jfloat offsetX, + jfloat offsetY, + jboolean doLeftAndRightSwapInRTL, + jboolean isRTL, + jfloat pixelDensity); + + void setProps(NativeMap *props); + + private: + mutable better::shared_mutex lifecycleMutex_; + const SurfaceHandler surfaceHandler_; + + jni::alias_ref jhybridobject_; + + static jni::local_ref initHybrid( + jni::alias_ref, + jint surfaceId, + jni::alias_ref moduleName); +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/react/renderer/scheduler/SurfaceHandler.cpp b/ReactCommon/react/renderer/scheduler/SurfaceHandler.cpp index acc6b36b195..b82a12d5bfa 100644 --- a/ReactCommon/react/renderer/scheduler/SurfaceHandler.cpp +++ b/ReactCommon/react/renderer/scheduler/SurfaceHandler.cpp @@ -124,6 +124,11 @@ SurfaceId SurfaceHandler::getSurfaceId() const noexcept { return parameters_.surfaceId; } +void SurfaceHandler::setSurfaceId(SurfaceId surfaceId) const noexcept { + std::unique_lock lock(parametersMutex_); + parameters_.surfaceId = surfaceId; +} + std::string SurfaceHandler::getModuleName() const noexcept { std::shared_lock lock(parametersMutex_); return parameters_.moduleName; diff --git a/ReactCommon/react/renderer/scheduler/SurfaceHandler.h b/ReactCommon/react/renderer/scheduler/SurfaceHandler.h index 4867608ddfc..892de7b4b8a 100644 --- a/ReactCommon/react/renderer/scheduler/SurfaceHandler.h +++ b/ReactCommon/react/renderer/scheduler/SurfaceHandler.h @@ -131,6 +131,7 @@ class SurfaceHandler final { #pragma mark - Accessors SurfaceId getSurfaceId() const noexcept; + void setSurfaceId(SurfaceId surfaceId) const noexcept; std::string getModuleName() const noexcept; /*