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
This commit is contained in:
Родитель
aab01608ba
Коммит
7b5277bb75
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
<T extends SizeMonitoringFrameLayout & MeasureSpecProvider> int addRootView(final T rootView);
|
||||
<T extends SizeMonitoringFrameLayout & MeasureSpecProvider> int addRootView(final T rootView, WritableMap initialProps, @Nullable String initialUITemplate);
|
||||
|
||||
/**
|
||||
* Unregisters a new root view.
|
||||
|
|
|
@ -363,6 +363,11 @@ public class UIManagerModule extends ReactContextBaseJavaModule
|
|||
return mUIImplementation.getProfiledBatchPerfCounters();
|
||||
}
|
||||
|
||||
public <T extends SizeMonitoringFrameLayout & MeasureSpecProvider> 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 <T extends SizeMonitoringFrameLayout & MeasureSpecProvider> 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();
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
#include <fb/glog_init.h>
|
||||
#include <fb/log.h>
|
||||
|
@ -61,6 +63,7 @@ class ProxyJavaScriptExecutorHolder : public HybridClass<ProxyJavaScriptExecutor
|
|||
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
return initialize(vm, [] {
|
||||
gloginit::initialize();
|
||||
FLAGS_minloglevel = 0;
|
||||
ProxyJavaScriptExecutorHolder::registerNatives();
|
||||
CatalystInstanceImpl::registerNatives();
|
||||
CxxModuleWrapperBase::registerNatives();
|
||||
|
|
|
@ -99,7 +99,12 @@ static const RawProps rawPropsFromDynamic(const folly::dynamic object) {
|
|||
return result;
|
||||
}
|
||||
|
||||
SharedShadowNode ComponentDescriptorRegistry::createNode(Tag tag, const std::string &viewName, Tag rootTag, const folly::dynamic &props, const SharedEventTarget &eventTarget) const {
|
||||
SharedShadowNode ComponentDescriptorRegistry::createNode(
|
||||
Tag tag,
|
||||
const std::string &viewName,
|
||||
Tag rootTag,
|
||||
const folly::dynamic &props,
|
||||
const SharedEventTarget &eventTarget) const {
|
||||
ComponentName componentName = componentNameByReactViewName(viewName);
|
||||
const SharedComponentDescriptor &componentDescriptor = (*this)[componentName];
|
||||
RawProps rawProps = rawPropsFromDynamic(props);
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
|
||||
#include "ReactBytecodeInterpreter.h"
|
||||
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include <fabric/components/view/ViewComponentDescriptor.h>
|
||||
#include <fabric/components/view/ViewProps.h>
|
||||
#include <fabric/components/view/ViewShadowNode.h>
|
||||
|
@ -18,10 +16,13 @@
|
|||
#include <fabric/debug/DebugStringConvertible.h>
|
||||
#include <fabric/debug/DebugStringConvertibleItem.h>
|
||||
#include <folly/json.h>
|
||||
#include <glog/logging.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
bool constexpr DEBUG_FLY = false;
|
||||
|
||||
struct RBCContext {
|
||||
const Tag rootTag;
|
||||
const std::vector<SharedShadowNode> &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<SharedShadowNode> nodes(commands.size() * 2);
|
||||
std::vector<folly::dynamic> 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{};
|
||||
|
|
|
@ -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<std::mutex> lock(mutex_);
|
||||
|
||||
auto shadowTree =
|
||||
std::make_unique<ShadowTree>(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>(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<std::mutex> lock(mutex_);
|
||||
const auto &shadowTree = shadowTreeRegistry_.at(surfaceId);
|
||||
assert(shadowTree);
|
||||
shadowTree->complete(
|
||||
std::make_shared<SharedShadowNodeList>(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<std::mutex> lock(mutex_);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче