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:
Spencer Ahrens 2018-11-05 15:32:47 -08:00 коммит произвёл Facebook Github Bot
Родитель aab01608ba
Коммит 7b5277bb75
10 изменённых файлов: 115 добавлений и 52 удалений

Просмотреть файл

@ -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 &param : 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