From 7b5277bb75100777bfd6eb686b1e895df1020c02 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Mon, 5 Nov 2018 15:32:47 -0800 Subject: [PATCH] mostly working on Android + OTA Summary: It works great on iOS, and mostly works on Android, and is now OTA'able as part of the screen config! Haven't done template view yet. One remaining issue: Layout is borked on Android. I'm guessing the issue has to do with the timing of setting the constraints in `updateRootLayoutSpecs` and calling `mBinding.startSurface` which actually builds the shadow tree. If I try to call `updateRootLayoutSpecs` earlier, it just crashes immediately. Here's the layout it spits out, which clearly has -440 for the x of 420006, which is the RCTText component, causing it to get cut off on the left of the screen: ``` updateLayoutMountItem for reactTag: 420006 x: -440, y: -13, width: 931, height: 78 updateLayoutMountItem for reactTag: 420010 x: 26, y: 79, width: 0, height: 1651 updateLayoutMountItem for reactTag: 420012 x: 0, y: 26, width: 0, height: 158 updateLayoutMountItem for reactTag: 420016 x: 0, y: 210, width: 454, height: 454 updateLayoutMountItem for reactTag: 420018 x: 454, y: 210, width: 455, height: 454 updateLayoutMountItem for reactTag: 420022 x: 0, y: 690, width: 454, height: 454 updateLayoutMountItem for reactTag: 420024 x: 454, y: 690, width: 455, height: 454 updateLayoutMountItem for reactTag: 420028 x: 0, y: 1171, width: 454, height: 454 updateLayoutMountItem for reactTag: 420030 x: 454, y: 1171, width: 455, height: 454 updateLayoutMountItem for reactTag: 420032 x: 0, y: 1651, width: 0, height: 0 ``` Reviewed By: mdvacca Differential Revision: D12813192 fbshipit-source-id: 450d646af4883ff25184141721351da67b091b7c --- React/Fabric/RCTScheduler.mm | 17 +++--- .../facebook/react/ReactInstanceManager.java | 10 +++- .../com/facebook/react/ReactRootView.java | 16 +++++- .../com/facebook/react/bridge/UIManager.java | 3 +- .../react/uimanager/UIManagerModule.java | 7 ++- .../src/main/jni/react/jni/OnLoad.cpp | 3 ++ .../uimanager/ComponentDescriptorRegistry.cpp | 7 ++- .../uimanager/ReactBytecodeInterpreter.cpp | 39 +++++++++----- ReactCommon/fabric/uimanager/Scheduler.cpp | 54 ++++++++++--------- ReactCommon/fabric/uimanager/Scheduler.h | 11 ++-- 10 files changed, 115 insertions(+), 52 deletions(-) diff --git a/React/Fabric/RCTScheduler.mm b/React/Fabric/RCTScheduler.mm index d9ccfe8efb..57d324e45a 100644 --- a/React/Fabric/RCTScheduler.mm +++ b/React/Fabric/RCTScheduler.mm @@ -63,13 +63,18 @@ private: layoutConstraints:(LayoutConstraints)layoutConstraints layoutContext:(LayoutContext)layoutContext; { + auto props = convertIdToFollyDynamic(initialProps); _scheduler->startSurface( - surfaceId, - RCTStringFromNSString(moduleName), - convertIdToFollyDynamic(initialProps), - layoutConstraints, - layoutContext - ); + surfaceId, + RCTStringFromNSString(moduleName), + props, + layoutConstraints, + layoutContext); + _scheduler->renderTemplateToSurface( + surfaceId, + props.getDefault("navigationConfig") + .getDefault("initialUITemplate", "") + .getString()); } - (void)stopSurfaceWithSurfaceId:(SurfaceId)surfaceId diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java index 8773bf83f2..d4a7e8efe9 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java @@ -35,6 +35,7 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; import android.net.Uri; +import android.os.Bundle; import android.os.Process; import android.support.v4.view.ViewCompat; import android.util.Log; @@ -45,6 +46,7 @@ import com.facebook.debug.tags.ReactDebugOverlayTags; import com.facebook.infer.annotation.Assertions; import com.facebook.infer.annotation.ThreadConfined; import com.facebook.infer.annotation.ThreadSafe; +import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.CatalystInstance; import com.facebook.react.bridge.CatalystInstanceImpl; import com.facebook.react.bridge.JSBundleLoader; @@ -63,6 +65,7 @@ import com.facebook.react.bridge.ReactMarker; import com.facebook.react.bridge.ReactMarkerConstants; import com.facebook.react.bridge.UIManager; import com.facebook.react.bridge.UiThreadUtil; +import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec; import com.facebook.react.common.LifecycleState; import com.facebook.react.common.ReactConstants; @@ -1033,7 +1036,12 @@ public class ReactInstanceManager { Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "attachRootViewToInstance"); UIManager uiManagerModule = UIManagerHelper.getUIManager(mCurrentReactContext, rootView.getUIManagerType()); - final int rootTag = uiManagerModule.addRootView(rootView); + @Nullable Bundle initialProperties = rootView.getAppProperties(); + final int rootTag = uiManagerModule.addRootView( + rootView, + initialProperties == null ? + new WritableNativeMap() : Arguments.fromBundle(initialProperties), + rootView.getInitialUITemplate()); rootView.setRootViewTag(rootTag); rootView.runApplication(); Systrace.beginAsyncSection( diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java b/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java index 0d98f225dd..8441065aa9 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java @@ -82,6 +82,7 @@ public class ReactRootView extends SizeMonitoringFrameLayout private @Nullable ReactInstanceManager mReactInstanceManager; private @Nullable String mJSModuleName; private @Nullable Bundle mAppProperties; + private @Nullable String mInitialUITemplate; private @Nullable CustomGlobalLayoutListener mCustomGlobalLayoutListener; private @Nullable ReactRootViewEventListener mRootViewEventListener; private int mRootViewTag; @@ -343,6 +344,13 @@ public class ReactRootView extends SizeMonitoringFrameLayout startReactApplication(reactInstanceManager, moduleName, null); } + /** + * {@see #startReactApplication(ReactInstanceManager, String, android.os.Bundle, String)} + */ + public void startReactApplication(ReactInstanceManager reactInstanceManager, String moduleName, @Nullable Bundle initialProperties) { + startReactApplication(reactInstanceManager, moduleName, initialProperties, null); + } + /** * Schedule rendering of the react component rendered by the JS application from the given JS * module (@{param moduleName}) using provided {@param reactInstanceManager} to attach to the @@ -352,7 +360,8 @@ public class ReactRootView extends SizeMonitoringFrameLayout public void startReactApplication( ReactInstanceManager reactInstanceManager, String moduleName, - @Nullable Bundle initialProperties) { + @Nullable Bundle initialProperties, + @Nullable String initialUITemplate) { Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "startReactApplication"); try { UiThreadUtil.assertOnUiThread(); @@ -367,6 +376,7 @@ public class ReactRootView extends SizeMonitoringFrameLayout mReactInstanceManager = reactInstanceManager; mJSModuleName = moduleName; mAppProperties = initialProperties; + mInitialUITemplate = initialUITemplate; if (!mReactInstanceManager.hasStartedCreatingInitialContext()) { mReactInstanceManager.createReactContextInBackground(); @@ -449,6 +459,10 @@ public class ReactRootView extends SizeMonitoringFrameLayout return mAppProperties; } + public @Nullable String getInitialUITemplate() { + return mInitialUITemplate; + } + public void setAppProperties(@Nullable Bundle appProperties) { UiThreadUtil.assertOnUiThread(); mAppProperties = appProperties; diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.java index 6d48243901..d367a6a9db 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.java @@ -7,6 +7,7 @@ package com.facebook.react.bridge; +import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.common.MeasureSpecProvider; import com.facebook.react.uimanager.common.SizeMonitoringFrameLayout; @@ -17,7 +18,7 @@ public interface UIManager extends JSIModule, PerformanceCounter { /** * Registers a new root view. */ - int addRootView(final T rootView); + int addRootView(final T rootView, WritableMap initialProps, @Nullable String initialUITemplate); /** * Unregisters a new root view. diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java index e0fa9fcefc..bdab2af5af 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java @@ -363,6 +363,11 @@ public class UIManagerModule extends ReactContextBaseJavaModule return mUIImplementation.getProfiledBatchPerfCounters(); } + public int addRootView( + final T rootView) { + return addRootView(rootView, null, null); + } + /** * Registers a new root view. JS can use the returned tag with manageChildren to add/remove * children to this view. @@ -374,7 +379,7 @@ public class UIManagerModule extends ReactContextBaseJavaModule */ @Override public int addRootView( - final T rootView) { + final T rootView, WritableMap initialProps, @Nullable String initialUITemplate) { Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "UIManagerModule.addRootView"); final int tag = ReactRootViewTagGenerator.getNextRootViewTag(); final ReactApplicationContext reactApplicationContext = getReactApplicationContext(); diff --git a/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp b/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp index 20b134a5c8..3767097d57 100644 --- a/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp +++ b/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp @@ -5,6 +5,8 @@ #include +#include + #include #include #include @@ -61,6 +63,7 @@ class ProxyJavaScriptExecutorHolder : public HybridClass - #include #include #include @@ -18,10 +16,13 @@ #include #include #include +#include namespace facebook { namespace react { +bool constexpr DEBUG_FLY = false; + struct RBCContext { const Tag rootTag; const std::vector &nodes; @@ -40,6 +41,7 @@ SharedShadowNode ReactBytecodeInterpreter::runCommand( const NativeModuleRegistry &nativeModuleRegistry) { const std::string &opcode = command[0].asString(); const int tagOffset = 420000; + // TODO: change to integer codes and a switch statement if (opcode == "createNode") { int tag = command[1].asInt(); const auto &type = command[2].asString(); @@ -55,7 +57,7 @@ SharedShadowNode ReactBytecodeInterpreter::runCommand( } } else if (opcode == "childSetNode") { LOG(INFO) - << "(stop) ReactBytecodeInterpreter inject serialized 'server rendered' view tree"; + << "(stop) UITemplateProcessor inject serialized 'server rendered' view tree"; return nodes[command[1].asInt()]; } else if (opcode == "loadNativeBool") { int registerNumber = command[1].asInt(); @@ -68,8 +70,7 @@ SharedShadowNode ReactBytecodeInterpreter::runCommand( if (conditionDynamic.isNull()) { // TODO: provide original command or command line? auto err = std::runtime_error( - "register " + command[1].asString() + - " wasn't loaded before access"); + "register " + command[1].asString() + " wasn't loaded before access"); throw err; } else if (conditionDynamic.type() != folly::dynamic::BOOL) { // TODO: provide original command or command line? @@ -104,6 +105,7 @@ SharedShadowNode ReactBytecodeInterpreter::buildShadowTree( const NativeModuleRegistry &nativeModuleRegistry) { LOG(INFO) << "(strt) ReactBytecodeInterpreter inject hardcoded 'server rendered' view tree"; + std::string content = jsonStr; for (const auto ¶m : params.items()) { const auto &key = param.first.asString(); @@ -117,17 +119,26 @@ SharedShadowNode ReactBytecodeInterpreter::buildShadowTree( std::vector nodes(commands.size() * 2); std::vector registers(32); for (const auto &command : commands) { - auto ret = runCommand( - command, - rootTag, - nodes, - registers, - componentDescriptorRegistry, - nativeModuleRegistry); - if (ret != nullptr) { - return ret; + try { + if (DEBUG_FLY) { + LOG(INFO) << "try to run command " << folly::toJson(command); + } + auto ret = runCommand( + command, + rootTag, + nodes, + registers, + componentDescriptorRegistry, + nativeModuleRegistry); + if (ret != nullptr) { + return ret; + } + } catch (const std::exception &e) { + LOG(ERROR) << " >>> Exception <<< running previous command '" + << folly::toJson(command) << "': '" << e.what() << "'"; } } + LOG(ERROR) << "react ui template missing childSetNode command :("; throw std::runtime_error( "Missing childSetNode command in template content:\n" + content); return SharedShadowNode{}; diff --git a/ReactCommon/fabric/uimanager/Scheduler.cpp b/ReactCommon/fabric/uimanager/Scheduler.cpp index b04ab1627a..04ca1cccc9 100644 --- a/ReactCommon/fabric/uimanager/Scheduler.cpp +++ b/ReactCommon/fabric/uimanager/Scheduler.cpp @@ -49,11 +49,8 @@ Scheduler::Scheduler(const SharedContextContainer &contextContainer) asynchronousEventBeatFactory); componentDescriptorRegistry_ = ComponentDescriptorFactory::buildRegistry( - eventDispatcher, contextContainer); - - uiManager_->setComponentDescriptorRegistry( - componentDescriptorRegistry_ - ); + eventDispatcher, contextContainer); + uiManager_->setComponentDescriptorRegistry(componentDescriptorRegistry_); uiManager_->setDelegate(this); } @@ -67,36 +64,45 @@ void Scheduler::startSurface( const std::string &moduleName, const folly::dynamic &initialProps, const LayoutConstraints &layoutConstraints, - const LayoutContext &layoutContext) { + const LayoutContext &layoutContext) const { std::lock_guard lock(mutex_); auto shadowTree = std::make_unique(surfaceId, layoutConstraints, layoutContext); shadowTree->setDelegate(this); - - LOG(INFO) << "initialProps in Scheduler::startSurface - type: " << initialProps.type() << initialProps.typeName() << initialProps; - - // TODO: Is this an ok place to do this? - if (initialProps.type() == folly::dynamic::OBJECT) { - auto serializedCommands = initialProps.find("serializedCommands"); - if (serializedCommands != initialProps.items().end()) { - NativeModuleRegistry nMR; - auto tree = ReactBytecodeInterpreter::buildShadowTree(serializedCommands->second.asString(), surfaceId, folly::dynamic::object(), *componentDescriptorRegistry_, nMR); - shadowTree->complete(std::make_shared(SharedShadowNodeList {tree})); - shadowTreeRegistry_.emplace(surfaceId, std::move(shadowTree)); - // TODO: hydrate rather than replace - #ifndef ANDROID - uiManager_->startSurface(surfaceId, moduleName, initialProps); - #endif - return; - } - } shadowTreeRegistry_.emplace(surfaceId, std::move(shadowTree)); + #ifndef ANDROID uiManager_->startSurface(surfaceId, moduleName, initialProps); #endif } +void Scheduler::renderTemplateToSurface( + SurfaceId surfaceId, + const std::string &uiTemplate) { + try { + if (uiTemplate.size() == 0) { + return; + } + NativeModuleRegistry nMR; + auto tree = ReactBytecodeInterpreter::buildShadowTree( + uiTemplate, + surfaceId, + folly::dynamic::object(), + *componentDescriptorRegistry_, + nMR); + + std::lock_guard lock(mutex_); + const auto &shadowTree = shadowTreeRegistry_.at(surfaceId); + assert(shadowTree); + shadowTree->complete( + std::make_shared(SharedShadowNodeList{tree})); + } catch (const std::exception &e) { + LOG(ERROR) << " >>>> EXCEPTION <<< rendering uiTemplate in " + << "Scheduler::renderTemplateToSurface: " << e.what(); + } +} + void Scheduler::stopSurface(SurfaceId surfaceId) const { std::lock_guard lock(mutex_); diff --git a/ReactCommon/fabric/uimanager/Scheduler.h b/ReactCommon/fabric/uimanager/Scheduler.h index 681b20f49f..4961dad728 100644 --- a/ReactCommon/fabric/uimanager/Scheduler.h +++ b/ReactCommon/fabric/uimanager/Scheduler.h @@ -37,7 +37,11 @@ class Scheduler final : public UIManagerDelegate, public ShadowTreeDelegate { const std::string &moduleName, const folly::dynamic &initialProps, const LayoutConstraints &layoutConstraints = {}, - const LayoutContext &layoutContext = {}); + const LayoutContext &layoutContext = {}) const; + + void renderTemplateToSurface( + SurfaceId surfaceId, + const std::string &uiTemplate); void stopSurface(SurfaceId surfaceId) const; @@ -99,8 +103,9 @@ class Scheduler final : public UIManagerDelegate, public ShadowTreeDelegate { SharedEventDispatcher eventDispatcher_; SharedContextContainer contextContainer_; - void uiManagerDidFinishTransactionWithoutLock(Tag rootTag, const SharedShadowNodeUnsharedList &rootChildNodes); - + void uiManagerDidFinishTransactionWithoutLock( + Tag rootTag, + const SharedShadowNodeUnsharedList &rootChildNodes); }; } // namespace react