Open source Fabric android

Summary: This diff open sources Fabric Android implementation and it extracts ComponentDescriptorFactory into a function that can be "injected" per application

Reviewed By: shergin

Differential Revision: D13616172

fbshipit-source-id: 7b7a6461216740b5a1ad5ebbead9e37de4570221
This commit is contained in:
David Vacca 2019-01-16 12:01:45 -08:00 committed by Facebook Github Bot
parent cfbb2278a0
commit b421b5f4bd
45 changed files with 2738 additions and 52 deletions

View File

@ -7,6 +7,7 @@
#import "RCTScheduler.h"
#import <react/uimanager/ComponentDescriptorFactory.h>
#import <react/uimanager/ContextContainer.h>
#import <react/uimanager/Scheduler.h>
#import <react/uimanager/SchedulerDelegate.h>
@ -49,7 +50,7 @@ private:
{
if (self = [super init]) {
_delegateProxy = std::make_shared<SchedulerDelegateProxy>((__bridge void *)self);
_scheduler = std::make_shared<Scheduler>(std::static_pointer_cast<ContextContainer>(contextContatiner));
_scheduler = std::make_shared<Scheduler>(std::static_pointer_cast<ContextContainer>(contextContatiner), getDefaultComponentRegistryFactory());
_scheduler->setDelegate(_delegateProxy.get());
}

View File

@ -4,10 +4,11 @@ rn_android_library(
name = "fabric",
srcs = glob([
"*.java",
"events/*.java",
"jsi/*.java",
"mounting/**/*.java",
]),
provided_deps = [
react_native_dep("third-party/android/support/v4:lib-support-v4"),
react_native_dep("third-party/android/support-new:support-v4"),
],
required_for_source_only_abi = True,
visibility = [
@ -15,17 +16,23 @@ rn_android_library(
],
deps = [
YOGA_TARGET,
react_native_dep("third-party/java/infer-annotations:infer-annotations"),
react_native_dep("java/com/facebook/systrace:systrace"),
react_native_dep("libraries/fbcore/src/main/java/com/facebook/common/logging:logging"),
react_native_dep("third-party/java/infer-annotations:infer-annotations"),
react_native_dep("third-party/java/jsr-305:jsr-305"),
react_native_dep("libraries/soloader/java/com/facebook/soloader:soloader"),
react_native_target("java/com/facebook/debug/tags:tags"),
react_native_target("java/com/facebook/debug/holder:holder"),
react_native_target("java/com/facebook/react/bridge:bridge"),
react_native_target("java/com/facebook/react/uimanager:uimanager"),
react_native_target("java/com/facebook/react/uimanager/annotations:annotations"),
react_native_target("java/com/facebook/react/fabric/jsi/jni:jni"),
react_native_target("java/com/facebook/react:react"),
react_native_target("java/com/facebook/react/module/annotations:annotations"),
react_native_target("java/com/facebook/react/common:common"),
react_native_target("java/com/facebook/react/modules/core:core"),
react_native_target("java/com/facebook/react/modules/i18nmanager:i18nmanager"),
react_native_target("java/com/facebook/react/common:common"),
react_native_target("java/com/facebook/react/uimanager:uimanager"),
react_native_target("java/com/facebook/react/views/view:view"),
react_native_target("java/com/facebook/react/touch:touch"),
],
exported_deps = [
],
)

View File

@ -8,6 +8,8 @@ package com.facebook.react.fabric;
import com.facebook.react.bridge.JavaScriptContextHolder;
import com.facebook.react.bridge.queue.MessageQueueThread;
import com.facebook.react.fabric.jsi.ComponentFactoryDelegate;
import com.facebook.react.fabric.jsi.EventBeatManager;
public interface FabricBinding {
@ -15,8 +17,9 @@ public interface FabricBinding {
void register(
JavaScriptContextHolder jsContext,
FabricBinder fabricBinder,
Object eventBeatManager,
MessageQueueThread jsMessageQueueThread);
EventBeatManager eventBeatManager,
MessageQueueThread jsMessageQueueThread,
ComponentFactoryDelegate componentFactoryDelegate);
void unregister();
}

View File

@ -0,0 +1,155 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* <p>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.uimanager.events.TouchesHelper.CHANGED_TOUCHES_KEY;
import static com.facebook.react.uimanager.events.TouchesHelper.TARGET_KEY;
import static com.facebook.react.uimanager.events.TouchesHelper.TOP_TOUCH_CANCEL_KEY;
import static com.facebook.react.uimanager.events.TouchesHelper.TOP_TOUCH_END_KEY;
import static com.facebook.react.uimanager.events.TouchesHelper.TOUCHES_KEY;
import android.annotation.TargetApi;
import android.os.Build;
import android.util.Pair;
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeArray;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.uimanager.events.RCTEventEmitter;
import com.facebook.systrace.Systrace;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.Nullable;
@TargetApi(Build.VERSION_CODES.ECLAIR)
public class FabricEventEmitter implements RCTEventEmitter {
private static final String TAG = FabricEventEmitter.class.getSimpleName();
private final FabricUIManager mUIManager;
public FabricEventEmitter(FabricUIManager uiManager) {
mUIManager = uiManager;
}
@Override
public void receiveEvent(int reactTag, String eventName, @Nullable WritableMap params) {
Systrace.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"FabricEventEmitter.receiveEvent('" + eventName + "')");
mUIManager.receiveEvent(reactTag, eventName, params);
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
@Override
public void receiveTouches(
String eventTopLevelType, WritableArray touches, WritableArray changedIndices) {
Pair<WritableArray, WritableArray> result =
TOP_TOUCH_END_KEY.equalsIgnoreCase(eventTopLevelType)
|| TOP_TOUCH_CANCEL_KEY.equalsIgnoreCase(eventTopLevelType)
? removeTouchesAtIndices(touches, changedIndices)
: touchSubsequence(touches, changedIndices);
WritableArray changedTouches = result.first;
touches = result.second;
for (int jj = 0; jj < changedTouches.size(); jj++) {
WritableMap touch = getWritableMap(changedTouches.getMap(jj));
// Touch objects can fulfill the role of `DOM` `Event` objects if we set
// the `changedTouches`/`touches`. This saves allocations.
touch.putArray(CHANGED_TOUCHES_KEY, copyWritableArray(changedTouches));
touch.putArray(TOUCHES_KEY, copyWritableArray(touches));
WritableMap nativeEvent = touch;
int rootNodeID = 0;
int target = nativeEvent.getInt(TARGET_KEY);
if (target < 1) {
FLog.e(TAG, "A view is reporting that a touch occurred on tag zero.");
} else {
rootNodeID = target;
}
receiveEvent(rootNodeID, eventTopLevelType, touch);
}
}
/** TODO T31905686 optimize this to avoid copying arrays */
private WritableArray copyWritableArray(WritableArray array) {
WritableNativeArray ret = new WritableNativeArray();
for (int i = 0; i < array.size(); i++) {
ret.pushMap(getWritableMap(array.getMap(i)));
}
return ret;
}
/**
* Destroys `touches` by removing touch objects at indices `indices`. This is to maintain
* compatibility with W3C touch "end" events, where the active touches don't include the set that
* has just been "ended".
*
* <p>This method was originally in ReactNativeRenderer.js
*
* <p>TODO: this method is a copy from ReactNativeRenderer.removeTouchesAtIndices and it needs to
* be rewritten in a more efficient way,
*
* @param touches {@link WritableArray} Deserialized touch objects.
* @param indices {WritableArray} Indices to remove from `touches`.
* @return {Array<Touch>} Subsequence of removed touch objects.
*/
private Pair<WritableArray, WritableArray> removeTouchesAtIndices(
WritableArray touches, WritableArray indices) {
WritableArray rippedOut = new WritableNativeArray();
// use an unsafe downcast to alias to nullable elements,
// so we can delete and then compact.
WritableArray tempTouches = new WritableNativeArray();
Set<Integer> rippedOutIndices = new HashSet<>();
for (int i = 0; i < indices.size(); i++) {
int index = indices.getInt(i);
rippedOut.pushMap(getWritableMap(touches.getMap(index)));
rippedOutIndices.add(index);
}
for (int j = 0; j < touches.size(); j++) {
if (!rippedOutIndices.contains(j)) {
tempTouches.pushMap(getWritableMap(touches.getMap(j)));
}
}
return new Pair<>(rippedOut, tempTouches);
}
/**
* Selects a subsequence of `Touch`es, without destroying `touches`.
*
* <p>This method was originally in ReactNativeRenderer.js
*
* @param touches {@link WritableArray} Deserialized touch objects.
* @param changedIndices {@link WritableArray} Indices by which to pull subsequence.
* @return {Array<Touch>} Subsequence of touch objects.
*/
private Pair<WritableArray, WritableArray> touchSubsequence(
WritableArray touches, WritableArray changedIndices) {
WritableArray result = new WritableNativeArray();
for (int i = 0; i < changedIndices.size(); i++) {
result.pushMap(getWritableMap(touches.getMap(changedIndices.getInt(i))));
}
return new Pair<>(result, touches);
}
/**
* TODO: this is required because the WritableNativeArray.getMap() returns a ReadableMap instead
* of the original writableMap. this will change in the near future.
*
* @param readableMap {@link ReadableMap} source map
*/
private WritableMap getWritableMap(ReadableMap readableMap) {
WritableNativeMap map = new WritableNativeMap();
map.merge(readableMap);
return map;
}
}

View File

@ -0,0 +1,69 @@
// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.react.fabric;
import com.facebook.react.fabric.jsi.Binding;
import com.facebook.react.fabric.jsi.ComponentFactoryDelegate;
import com.facebook.react.fabric.jsi.EventBeatManager;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.JSIModuleProvider;
import com.facebook.react.bridge.JavaScriptContextHolder;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.UIManager;
import com.facebook.react.bridge.queue.MessageQueueThread;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.systrace.Systrace;
public class FabricJSIModuleProvider implements JSIModuleProvider<UIManager> {
private final ReactInstanceManager mReactInstanceManager;
private final JavaScriptContextHolder mJSContext;
private final ReactApplicationContext mReactApplicationContext;
private final ComponentFactoryDelegate mComponentFactoryDelegate;
public FabricJSIModuleProvider(
ReactInstanceManager reactInstanceManager,
ReactApplicationContext reactApplicationContext,
JavaScriptContextHolder jsContext,
ComponentFactoryDelegate componentFactoryDelegate) {
mReactInstanceManager = reactInstanceManager;
mReactApplicationContext = reactApplicationContext;
mJSContext = jsContext;
mComponentFactoryDelegate = componentFactoryDelegate;
}
@Override
public UIManager get() {
final EventBeatManager eventBeatManager =
new EventBeatManager(mJSContext, mReactApplicationContext);
final UIManager uiManager = createUIManager(eventBeatManager);
Systrace.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "FabricJSIModuleProvider.registerBinding");
final FabricBinding binding = new Binding();
MessageQueueThread jsMessageQueueThread =
mReactApplicationContext
.getCatalystInstance()
.getReactQueueConfiguration()
.getJSQueueThread();
binding.register(mJSContext, (FabricBinder) uiManager, eventBeatManager, jsMessageQueueThread,
mComponentFactoryDelegate);
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
return uiManager;
}
private UIManager createUIManager(EventBeatManager eventBeatManager) {
Systrace.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "FabricJSIModuleProvider.createUIManager");
UIManagerModule nativeModule = mReactApplicationContext.getNativeModule(UIManagerModule.class);
EventDispatcher eventDispatcher = nativeModule.getEventDispatcher();
FabricUIManager fabricUIManager =
new FabricUIManager(
mReactApplicationContext,
nativeModule.getViewManagerRegistry_DO_NOT_USE(),
eventDispatcher,
eventBeatManager);
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
return fabricUIManager;
}
}

View File

@ -0,0 +1,422 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* <p>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 static com.facebook.react.fabric.mounting.LayoutMetricsConversions.getYogaMeasureMode;
import static com.facebook.react.fabric.mounting.LayoutMetricsConversions.getYogaSize;
import static com.facebook.infer.annotation.ThreadConfined.UI;
import static com.facebook.react.uimanager.common.UIManagerType.FABRIC;
import android.annotation.SuppressLint;
import android.support.annotation.GuardedBy;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import com.facebook.common.logging.FLog;
import com.facebook.react.fabric.jsi.Binding;
import com.facebook.react.fabric.jsi.EventBeatManager;
import com.facebook.react.fabric.jsi.EventEmitterWrapper;
import com.facebook.react.fabric.jsi.FabricSoLoader;
import com.facebook.react.fabric.mounting.MountingManager;
import com.facebook.react.fabric.mounting.mountitems.BatchMountItem;
import com.facebook.react.fabric.mounting.mountitems.CreateMountItem;
import com.facebook.react.fabric.mounting.mountitems.DeleteMountItem;
import com.facebook.react.fabric.mounting.mountitems.DispatchCommandMountItem;
import com.facebook.react.fabric.mounting.mountitems.InsertMountItem;
import com.facebook.react.fabric.mounting.mountitems.MountItem;
import com.facebook.react.fabric.mounting.mountitems.RemoveMountItem;
import com.facebook.react.fabric.mounting.mountitems.UpdateEventEmitterMountItem;
import com.facebook.react.fabric.mounting.mountitems.UpdateLayoutMountItem;
import com.facebook.react.fabric.mounting.mountitems.UpdateLocalDataMountItem;
import com.facebook.react.fabric.mounting.mountitems.UpdatePropsMountItem;
import com.facebook.infer.annotation.Assertions;
import com.facebook.infer.annotation.ThreadConfined;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.bridge.GuardedRunnable;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.NativeMap;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableNativeMap;
import com.facebook.react.bridge.UIManager;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.modules.core.ReactChoreographer;
import com.facebook.react.uimanager.ReactRootViewTagGenerator;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewManagerPropertyUpdater;
import com.facebook.react.uimanager.ViewManagerRegistry;
import com.facebook.react.uimanager.common.MeasureSpecProvider;
import com.facebook.react.uimanager.common.SizeMonitoringFrameLayout;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.systrace.Systrace;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@SuppressLint("MissingNativeLoadLibrary")
public class FabricUIManager implements UIManager, FabricBinder<Binding>, LifecycleEventListener {
private static final String TAG = FabricUIManager.class.getSimpleName();
private static final Map<String, String> sComponentNames = new HashMap<>();
static {
FabricSoLoader.staticInit();
// TODO T31905686: unify component names between JS - Android - iOS - C++
sComponentNames.put("View", "RCTView");
sComponentNames.put("Image", "RCTImageView");
sComponentNames.put("ScrollView", "RCTScrollView");
sComponentNames.put("ReactPerformanceLoggerFlag", "ReactPerformanceLoggerFlag");
sComponentNames.put("Paragraph", "RCTText");
sComponentNames.put("Text", "RCText");
sComponentNames.put("RawText", "RCTRawText");
sComponentNames.put("ActivityIndicatorView", "AndroidProgressBar");
sComponentNames.put("ShimmeringView", "RKShimmeringView");
sComponentNames.put("TemplateView", "RCTTemplateView");
}
private Binding mBinding;
private final ReactApplicationContext mReactApplicationContext;
private final MountingManager mMountingManager;
private final EventDispatcher mEventDispatcher;
private final ConcurrentHashMap<Integer, ThemedReactContext> mReactContextForRootTag =
new ConcurrentHashMap<>();
private final EventBeatManager mEventBeatManager;
private final Object mMountItemsLock = new Object();
@GuardedBy("mMountItemsLock")
private List<MountItem> mMountItems = new ArrayList<>();
@ThreadConfined(UI)
private final DispatchUIFrameCallback mDispatchUIFrameCallback;
@ThreadConfined(UI)
private boolean mIsMountingEnabled = true;
public FabricUIManager(
ReactApplicationContext reactContext,
ViewManagerRegistry viewManagerRegistry,
EventDispatcher eventDispatcher,
EventBeatManager eventBeatManager) {
mDispatchUIFrameCallback = new DispatchUIFrameCallback(reactContext);
mReactApplicationContext = reactContext;
mMountingManager = new MountingManager(viewManagerRegistry);
mEventDispatcher = eventDispatcher;
mEventBeatManager = eventBeatManager;
mReactApplicationContext.addLifecycleEventListener(this);
}
@Override
public <T extends SizeMonitoringFrameLayout & MeasureSpecProvider> int addRootView(
final T rootView, final WritableMap initialProps, final @Nullable String initialUITemplate) {
final int rootTag = ReactRootViewTagGenerator.getNextRootViewTag();
ThemedReactContext reactContext =
new ThemedReactContext(mReactApplicationContext, rootView.getContext());
mMountingManager.addRootView(rootTag, rootView);
mReactContextForRootTag.put(rootTag, reactContext);
mBinding.startSurface(rootTag, (NativeMap) initialProps);
updateRootLayoutSpecs(rootTag, rootView.getWidthMeasureSpec(), rootView.getHeightMeasureSpec());
if (initialUITemplate != null) {
mBinding.renderTemplateToSurface(rootTag, initialUITemplate);
}
return rootTag;
}
/** Method called when an event has been dispatched on the C++ side. */
@DoNotStrip
public void onRequestEventBeat() {
mEventDispatcher.dispatchAllEvents();
}
@Override
public void removeRootView(int reactRootTag) {
// TODO T31905686: integrate with the unmounting of Fabric React Renderer.
mMountingManager.removeRootView(reactRootTag);
mReactContextForRootTag.remove(reactRootTag);
}
@DoNotStrip
@SuppressWarnings("unused")
private MountItem createMountItem(
String componentName, int reactRootTag, int reactTag, boolean isVirtual) {
String component = sComponentNames.get(componentName);
if (component == null) {
throw new IllegalArgumentException("Unable to find component with name " + componentName);
}
ThemedReactContext reactContext = mReactContextForRootTag.get(reactRootTag);
if (reactContext == null) {
throw new IllegalArgumentException("Unable to find ReactContext for root: " + reactRootTag);
}
return new CreateMountItem(reactContext, component, reactTag, isVirtual);
}
@Override
public void initialize() {
mEventDispatcher.registerEventEmitter(FABRIC, new FabricEventEmitter(this));
mEventDispatcher.addBatchEventDispatchedListener(mEventBeatManager);
}
@Override
public void onCatalystInstanceDestroy() {
mEventDispatcher.removeBatchEventDispatchedListener(mEventBeatManager);
mEventDispatcher.unregisterEventEmitter(FABRIC);
mBinding.unregister();
ViewManagerPropertyUpdater.clear();
}
@DoNotStrip
private void preallocateView(final int rootTag, final String componentName) {
UiThreadUtil.runOnUiThread(
new GuardedRunnable(mReactApplicationContext) {
@Override
public void runGuarded() {
ThemedReactContext context =
Assertions.assertNotNull(mReactContextForRootTag.get(rootTag));
String component = sComponentNames.get(componentName);
Assertions.assertNotNull(component);
mMountingManager.preallocateView(context, component);
}
});
}
@DoNotStrip
@SuppressWarnings("unused")
private MountItem removeMountItem(int reactTag, int parentReactTag, int index) {
return new RemoveMountItem(reactTag, parentReactTag, index);
}
@DoNotStrip
@SuppressWarnings("unused")
private MountItem insertMountItem(int reactTag, int parentReactTag, int index) {
return new InsertMountItem(reactTag, parentReactTag, index);
}
@DoNotStrip
@SuppressWarnings("unused")
private MountItem deleteMountItem(int reactTag) {
return new DeleteMountItem(reactTag);
}
@DoNotStrip
@SuppressWarnings("unused")
private MountItem updateLayoutMountItem(int reactTag, int x, int y, int width, int height) {
return new UpdateLayoutMountItem(reactTag, x, y, width, height);
}
@DoNotStrip
@SuppressWarnings("unused")
private MountItem updatePropsMountItem(int reactTag, ReadableNativeMap map) {
return new UpdatePropsMountItem(reactTag, map);
}
@DoNotStrip
@SuppressWarnings("unused")
private MountItem updateLocalDataMountItem(int reactTag, ReadableNativeMap newLocalData) {
return new UpdateLocalDataMountItem(reactTag, newLocalData);
}
@DoNotStrip
@SuppressWarnings("unused")
private MountItem updateEventEmitterMountItem(int reactTag, Object eventEmitter) {
return new UpdateEventEmitterMountItem(reactTag, (EventEmitterWrapper) eventEmitter);
}
@DoNotStrip
@SuppressWarnings("unused")
private MountItem createBatchMountItem(MountItem[] items, int size) {
return new BatchMountItem(items, size);
}
@DoNotStrip
@SuppressWarnings("unused")
private long measure(
String componentName,
ReadableNativeMap localData,
ReadableNativeMap props,
int minWidth,
int maxWidth,
int minHeight,
int maxHeight) {
return mMountingManager.measure(
mReactApplicationContext,
componentName,
localData,
props,
getYogaSize(minWidth, maxWidth),
getYogaMeasureMode(minWidth, maxWidth),
getYogaSize(minHeight, maxHeight),
getYogaMeasureMode(minHeight, maxHeight));
}
/**
* This method enqueues UI operations directly to the UI thread. This might change in the future
* to enforce execution order using {@link ReactChoreographer#CallbackType}.
*/
@DoNotStrip
@SuppressWarnings("unused")
private void scheduleMountItems(final MountItem mountItems) {
synchronized (mMountItemsLock) {
mMountItems.add(mountItems);
}
if (UiThreadUtil.isOnUiThread()) {
flushMountItems();
}
}
@UiThread
private void flushMountItems() {
if (!mIsMountingEnabled) {
FLog.w(
ReactConstants.TAG,
"Not flushing pending UI operations because of previously thrown Exception");
return;
}
try {
List<MountItem> mountItemsToDispatch;
synchronized (mMountItemsLock) {
if (mMountItems.isEmpty()) {
return;
}
mountItemsToDispatch = mMountItems;
mMountItems = new ArrayList<>();
}
Systrace.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"FabricUIManager::mountViews (" + mountItemsToDispatch.size() + " batches)");
for (MountItem mountItem : mountItemsToDispatch) {
mountItem.execute(mMountingManager);
}
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
} catch (Exception ex) {
FLog.i(ReactConstants.TAG, "Exception thrown when executing UIFrameGuarded", ex);
mIsMountingEnabled = false;
throw ex;
}
}
@Override
public void setBinding(Binding binding) {
mBinding = binding;
}
/**
* Updates the layout metrics of the root view based on the Measure specs received by parameters.
*/
@Override
public void updateRootLayoutSpecs(
final int rootTag, final int widthMeasureSpec, final int heightMeasureSpec) {
// TODO T31905686: this should not run in a different thread.
// This is a workaround because a race condition that happens in core of RN.
// We are analyzing this and fixing it as part of another diff.
mReactApplicationContext.runOnJSQueueThread(
new GuardedRunnable(mReactApplicationContext) {
@Override
public void runGuarded() {
mBinding.setConstraints(
rootTag,
getMinSize(widthMeasureSpec),
getMaxSize(widthMeasureSpec),
getMinSize(heightMeasureSpec),
getMaxSize(heightMeasureSpec));
}
});
}
public void receiveEvent(int reactTag, String eventName, @Nullable WritableMap params) {
EventEmitterWrapper eventEmitter = mMountingManager.getEventEmitter(reactTag);
if (eventEmitter == null) {
// This can happen if the view has disappeared from the screen (because of async events)
FLog.d(TAG, "Unable to invoke event: " + eventName + " for reactTag: " + reactTag);
return;
}
eventEmitter.invoke(eventName, params);
}
@Override
public void onHostResume() {
ReactChoreographer.getInstance()
.postFrameCallback(ReactChoreographer.CallbackType.DISPATCH_UI, mDispatchUIFrameCallback);
}
@Override
public void onHostPause() {
ReactChoreographer.getInstance()
.removeFrameCallback(ReactChoreographer.CallbackType.DISPATCH_UI, mDispatchUIFrameCallback);
}
@Override
public void onHostDestroy() {}
@Override
public void dispatchCommand(
final int reactTag, final int commandId, final ReadableArray commandArgs) {
scheduleMountItems(new DispatchCommandMountItem(reactTag, commandId, commandArgs));
}
@Override
public void setJSResponder(int reactTag, boolean blockNativeResponder) {
// do nothing for now.
}
@Override
public void clearJSResponder() {
// do nothing for now.
}
@Override
public void profileNextBatch() {
// do nothing for now.
}
@Override
public Map<String, Long> getPerformanceCounters() {
return new HashMap<>();
}
private class DispatchUIFrameCallback extends GuardedFrameCallback {
private DispatchUIFrameCallback(ReactContext reactContext) {
super(reactContext);
}
@Override
public void doFrameGuarded(long frameTimeNanos) {
if (!mIsMountingEnabled) {
FLog.w(
ReactConstants.TAG,
"Not flushing pending UI operations because of previously thrown Exception");
return;
}
try {
flushMountItems();
} catch (Exception ex) {
FLog.i(ReactConstants.TAG, "Exception thrown when executing UIFrameGuarded", ex);
mIsMountingEnabled = false;
throw ex;
} finally {
ReactChoreographer.getInstance()
.postFrameCallback(
ReactChoreographer.CallbackType.DISPATCH_UI, mDispatchUIFrameCallback);
}
}
}
}

View File

@ -0,0 +1,34 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* <p>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.ReactContext;
import com.facebook.react.modules.core.ChoreographerCompat;
public abstract class GuardedFrameCallback extends ChoreographerCompat.FrameCallback {
private final ReactContext mReactContext;
protected GuardedFrameCallback(ReactContext reactContext) {
mReactContext = reactContext;
}
@Override
public final void doFrame(long frameTimeNanos) {
try {
doFrameGuarded(frameTimeNanos);
} catch (RuntimeException e) {
mReactContext.handleException(e);
}
}
/**
* Like the standard doFrame but RuntimeExceptions will be caught and passed to {@link
* com.facebook.react.bridge.ReactContext#handleException(RuntimeException)}.
*/
protected abstract void doFrameGuarded(long frameTimeNanos);
}

View File

@ -0,0 +1,72 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* <p>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.jsi;
import android.annotation.SuppressLint;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.bridge.JavaScriptContextHolder;
import com.facebook.react.bridge.NativeMap;
import com.facebook.react.bridge.queue.MessageQueueThread;
import com.facebook.react.fabric.FabricBinder;
import com.facebook.react.fabric.FabricBinding;
import com.facebook.react.uimanager.PixelUtil;
@DoNotStrip
@SuppressLint("MissingNativeLoadLibrary")
public class Binding implements FabricBinding {
static {
FabricSoLoader.staticInit();
}
@DoNotStrip private final HybridData mHybridData;
private static native HybridData initHybrid();
public Binding() {
mHybridData = initHybrid();
}
private native void installFabricUIManager(
long jsContextNativePointer,
Object uiManager,
EventBeatManager eventBeatManager,
MessageQueueThread jsMessageQueueThread,
ComponentFactoryDelegate componentsRegistry);
public native void startSurface(int surfaceId, NativeMap initialProps);
public native void renderTemplateToSurface(int surfaceId, String uiTemplate);
public native void stopSurface(int surfaceId);
public native void setPixelDensity(float pointScaleFactor);
public native void setConstraints(
int rootTag, float minWidth, float maxWidth, float minHeight, float maxHeight);
@Override
public void register(
JavaScriptContextHolder jsContext,
FabricBinder fabricModule,
EventBeatManager eventBeatManager,
MessageQueueThread jsMessageQueueThread,
ComponentFactoryDelegate componentFactoryDelegate) {
fabricModule.setBinding(this);
installFabricUIManager(
jsContext.get(), fabricModule, eventBeatManager, jsMessageQueueThread, componentFactoryDelegate);
setPixelDensity(PixelUtil.getDisplayMetricDensity());
}
private native void uninstallFabricUIManager();
@Override
public void unregister() {
uninstallFabricUIManager();
}
}

View File

@ -0,0 +1,27 @@
// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.react.fabric.jsi;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.soloader.SoLoader;
import com.facebook.react.fabric.jsi.FabricSoLoader;
@DoNotStrip
public class ComponentFactoryDelegate {
static {
FabricSoLoader.staticInit();
}
@DoNotStrip
private final HybridData mHybridData;
@DoNotStrip
private static native HybridData initHybrid();
public ComponentFactoryDelegate() {
mHybridData = initHybrid();
}
}

View File

@ -0,0 +1,25 @@
// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.react.fabric.jsi;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.soloader.SoLoader;
@DoNotStrip
public class ComponentRegistry {
static {
FabricSoLoader.staticInit();
}
private final HybridData mHybridData;
@DoNotStrip
private static native HybridData initHybrid();
public ComponentRegistry() {
mHybridData = initHybrid();
}
}

View File

@ -0,0 +1,61 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* <p>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.jsi;
import android.annotation.SuppressLint;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.bridge.JavaScriptContextHolder;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.events.BatchEventDispatchedListener;
import com.facebook.react.fabric.jsi.FabricSoLoader;
/**
* Class that acts as a proxy between the list of EventBeats registered in C++ and the Android side.
*/
@SuppressLint("MissingNativeLoadLibrary")
public class EventBeatManager implements BatchEventDispatchedListener {
static {
FabricSoLoader.staticInit();
}
@DoNotStrip private final HybridData mHybridData;
private final ReactApplicationContext mReactApplicationContext;
private static native HybridData initHybrid(long jsContext);
private native void beat();
public EventBeatManager(
JavaScriptContextHolder jsContext, ReactApplicationContext reactApplicationContext) {
mHybridData = initHybrid(jsContext.get());
mReactApplicationContext = reactApplicationContext;
}
@Override
public void onBatchEventDispatched() {
dispatchEventsAsync();
}
/**
* Induce a beat in the AsyncEventBeat, calling the JNI method {@link #beat()} in the JS thread.
*/
private void dispatchEventsAsync() {
if (mReactApplicationContext.isOnJSQueueThread()) {
beat();
} else {
mReactApplicationContext.runOnJSQueueThread(
new Runnable() {
@Override
public void run() {
beat();
}
});
}
}
}

View File

@ -0,0 +1,49 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* <p>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.jsi;
import android.annotation.SuppressLint;
import android.support.annotation.Nullable;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.bridge.NativeMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.fabric.jsi.FabricSoLoader;
/**
* This class holds reference to the C++ EventEmitter object. Instances of this class are created on
* the Bindings.cpp, where the pointer to the C++ event emitter is set.
*/
@SuppressLint("MissingNativeLoadLibrary")
public class EventEmitterWrapper {
static {
FabricSoLoader.staticInit();
}
@DoNotStrip private final HybridData mHybridData;
private static native HybridData initHybrid();
private EventEmitterWrapper() {
mHybridData = initHybrid();
}
private native void invokeEvent(String eventName, NativeMap params);
/**
* Invokes the execution of the C++ EventEmitter.
*
* @param eventName {@link String} name of the event to execute.
* @param params {@link WritableMap} payload of the event
*/
public void invoke(String eventName, @Nullable WritableMap params) {
NativeMap payload = params == null ? new WritableNativeMap() : (NativeMap) params;
invokeEvent(eventName, payload);
}
}

View File

@ -0,0 +1,28 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* <p>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.jsi;
import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE;
import com.facebook.soloader.SoLoader;
import com.facebook.systrace.Systrace;
public class FabricSoLoader {
private static boolean sDidInit = false;
public static synchronized void staticInit() {
if (sDidInit) {
return;
}
sDidInit = true;
Systrace.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "FabricSoLoader.staticInit::load:fabricjni");
SoLoader.loadLibrary("fabricjni");
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
}

View File

@ -0,0 +1,59 @@
// Copyright 2004-present Facebook. All Rights Reserved.
// 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 "EventBeatManager.h"
#include <jsi/jsi.h>
#include <react/events/EventBeat.h>
namespace facebook {
namespace react {
namespace {
class AsyncEventBeat:
public EventBeat {
private:
EventBeatManager *eventBeatManager_;
jsi::Runtime *runtime_;
jni::global_ref<jobject> javaUIManager_;
public:
friend class EventBeatManager;
AsyncEventBeat(EventBeatManager* eventBeatManager, jsi::Runtime *runtime, jni::global_ref<jobject> javaUIManager) {
eventBeatManager_ = eventBeatManager;
runtime_ = runtime;
javaUIManager_ = javaUIManager;
eventBeatManager->registerEventBeat(this);
}
~AsyncEventBeat() {
eventBeatManager_->unregisterEventBeat(this);
}
void induce() const override {
beat(*runtime_);
}
void request() const override {
bool alreadyRequested = isRequested_;
EventBeat::request();
if (!alreadyRequested) {
// Notifies java side that an event will be dispatched (e.g. LayoutEvent)
static auto onRequestEventBeat =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<void()>("onRequestEventBeat");
onRequestEventBeat(javaUIManager_);
}
}
};
}
}
}

View File

@ -0,0 +1,47 @@
load("@fbsource//tools/build_defs:glob_defs.bzl", "subdir_glob")
load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "FBJNI_TARGET", "react_native_target", "react_native_xplat_target", "rn_xplat_cxx_library")
rn_xplat_cxx_library(
name = "jni",
srcs = glob(["*.cpp"]),
headers = glob(["*.h"]),
header_namespace = "",
exported_headers = subdir_glob(
[
("", "**/*.h"),
],
prefix = "react/fabric",
),
compiler_flags = [
"-Wall",
"-fexceptions",
"-std=gnu++1y",
"-frtti",
],
fbandroid_allow_jni_merging = True,
platforms = (ANDROID),
preprocessor_flags = [
"-DLOG_TAG=\"ReactNative\"",
"-DWITH_FBSYSTRACE=1",
],
soname = "libfabricjni.$(ext)",
visibility = ["PUBLIC"],
deps = [
react_native_xplat_target("config:config"),
react_native_xplat_target("fabric/uimanager:uimanager"),
react_native_xplat_target("fabric/components/activityindicator:activityindicator"),
react_native_xplat_target("fabric/components/scrollview:scrollview"),
react_native_xplat_target("fabric/components/image:image"),
react_native_xplat_target("fabric/components/text:text"),
react_native_target("jni/react/jni:jni"),
"xplat//ReactNative/fabric/components/ReactPerformanceLogger:ReactPerformanceLogger",
"xplat//ReactNative/fabric/components/ShimmeringView:ShimmeringView",
"xplat//ReactNative/fabric/components/TemplateView:TemplateView",
"xplat//fbsystrace:fbsystrace",
"xplat//folly:molly",
"xplat//jsi:JSIDynamic",
"xplat//jsi:jsi",
"xplat//third-party/linker_lib:atomic",
FBJNI_TARGET,
],
)

View File

@ -0,0 +1,376 @@
// Copyright 2004-present Facebook. All Rights Reserved.
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
#include "AsyncEventBeat.h"
#include "Binding.h"
#include "EventEmitterWrapper.h"
#include <android/log.h>
#include <fb/fbjni.h>
#include <jsi/jsi.h>
#include <jsi/JSIDynamic.h>
#include <react/components/scrollview/ScrollViewProps.h>
#include <react/config/ReactNativeConfig.h>
#include <react/debug/SystraceSection.h>
#include <react/events/EventEmitter.h>
#include <react/events/EventBeat.h>
#include <react/uimanager/ComponentDescriptorFactory.h>
#include <react/uimanager/ContextContainer.h>
#include <react/uimanager/primitives.h>
#include <react/uimanager/Scheduler.h>
#include <react/uimanager/SchedulerDelegate.h>
using namespace facebook::jni;
using namespace facebook::jsi;
namespace facebook {
namespace react {
namespace {
struct JMountItem : public JavaClass<JMountItem> {
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/fabric/mounting/mountitems/MountItem;";
};
static constexpr auto UIManagerJavaDescriptor = "com/facebook/react/fabric/FabricUIManager";
}
jni::local_ref<Binding::jhybriddata> Binding::initHybrid(
jni::alias_ref<jclass>) {
return makeCxxInstance();
}
void Binding::startSurface(jint surfaceId, NativeMap *initialProps) {
if (scheduler_) {
scheduler_->startSurface(surfaceId, "", initialProps->consume());
}
}
void Binding::renderTemplateToSurface(jint surfaceId, jstring uiTemplate) {
if (scheduler_) {
auto env = Environment::current();
const char *nativeString = env->GetStringUTFChars(uiTemplate, JNI_FALSE);
scheduler_->renderTemplateToSurface(surfaceId, nativeString);
env->ReleaseStringUTFChars(uiTemplate, nativeString);
}
}
void Binding::stopSurface(jint surfaceId){
if (scheduler_) {
scheduler_->stopSurface(surfaceId);
}
}
void Binding::setConstraints(jint rootTag, jfloat minWidth, jfloat maxWidth, jfloat minHeight, jfloat maxHeight) {
if (scheduler_) {
auto minimumSize = Size {minWidth / pointScaleFactor_, minHeight / pointScaleFactor_};
auto maximumSize = Size {maxWidth / pointScaleFactor_, maxHeight / pointScaleFactor_};
LayoutContext context;
context.pointScaleFactor = { pointScaleFactor_ };
LayoutConstraints constraints = {};
constraints.minimumSize = minimumSize;
constraints.maximumSize = maximumSize;
scheduler_->constraintSurfaceLayout(rootTag, constraints, context);
}
}
void Binding::installFabricUIManager(jlong jsContextNativePointer, jni::alias_ref<jobject> javaUIManager, EventBeatManager* eventBeatManager, jni::alias_ref<JavaMessageQueueThread::javaobject> jsMessageQueueThread, ComponentFactoryDelegate* componentsRegistry) {
Runtime* runtime = (Runtime*) jsContextNativePointer;
javaUIManager_ = make_global(javaUIManager);
SharedContextContainer contextContainer = std::make_shared<ContextContainer>();
auto sharedJSMessageQueueThread = std::make_shared<JMessageQueueThread> (jsMessageQueueThread);
RuntimeExecutor runtimeExecutor = [runtime, sharedJSMessageQueueThread](std::function<void(facebook::jsi::Runtime &runtime)> &&callback) {
sharedJSMessageQueueThread->runOnQueue([runtime, callback = std::move(callback)]() {
callback(*runtime);
});
};
// TODO: T31905686 Create synchronous Event Beat
jni::global_ref<jobject> localJavaUIManager = javaUIManager_;
EventBeatFactory synchronousBeatFactory = [eventBeatManager, runtime, localJavaUIManager]() mutable {
return std::make_unique<AsyncEventBeat>(eventBeatManager, runtime, localJavaUIManager);
};
EventBeatFactory asynchronousBeatFactory = [eventBeatManager, runtime, localJavaUIManager]() mutable {
return std::make_unique<AsyncEventBeat>(eventBeatManager, runtime, localJavaUIManager);
};
// TODO: Provide non-empty impl for ReactNativeConfig.
std::shared_ptr<const ReactNativeConfig> config = std::make_shared<const EmptyReactNativeConfig>();
contextContainer->registerInstance(config, "ReactNativeConfig");
contextContainer->registerInstance<EventBeatFactory>(synchronousBeatFactory, "synchronous");
contextContainer->registerInstance<EventBeatFactory>(asynchronousBeatFactory, "asynchronous");
contextContainer->registerInstance(javaUIManager_, "FabricUIManager");
contextContainer->registerInstance(runtimeExecutor, "runtime-executor");
scheduler_ = std::make_shared<Scheduler>(contextContainer, componentsRegistry->buildRegistryFunction);
scheduler_->setDelegate(this);
}
void Binding::uninstallFabricUIManager() {
scheduler_ = nullptr;
javaUIManager_ = nullptr;
}
local_ref<JString> getPlatformComponentName(const ShadowView &shadowView) {
local_ref<JString> componentName;
auto newViewProps = std::dynamic_pointer_cast<const ScrollViewProps>(shadowView.props);
if (newViewProps && newViewProps->yogaStyle.flexDirection == YGFlexDirectionRow) {
componentName = make_jstring("AndroidHorizontalScrollView");
} else {
componentName = make_jstring(shadowView.componentName);
}
return componentName;
}
local_ref<JMountItem::javaobject> createCreateMountItem(const jni::global_ref<jobject> &javaUIManager, const ShadowViewMutation &mutation, const Tag rootTag) {
static auto createJavaInstruction =
jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<alias_ref<JMountItem>(jstring,jint,jint,jboolean)>("createMountItem");
auto newChildShadowView = mutation.newChildShadowView;
local_ref<JString> componentName = getPlatformComponentName(newChildShadowView);
jboolean isVirtual = newChildShadowView.layoutMetrics == EmptyLayoutMetrics;
return createJavaInstruction(javaUIManager, componentName.get(), rootTag, newChildShadowView.tag, isVirtual);
}
local_ref<JMountItem::javaobject> createUpdateEventEmitterMountItem(const jni::global_ref<jobject> &javaUIManager, const ShadowViewMutation &mutation) {
if (!mutation.newChildShadowView.eventEmitter) {
return nullptr;
}
SharedEventEmitter eventEmitter = mutation.newChildShadowView.eventEmitter;
// Do not hold a reference to javaEventEmitter from the C++ side.
auto javaEventEmitter = EventEmitterWrapper::newObjectJavaArgs();
EventEmitterWrapper* cEventEmitter = cthis(javaEventEmitter);
cEventEmitter->eventEmitter = eventEmitter;
static auto updateEventEmitterInstruction =
jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<alias_ref<JMountItem>(jint, jobject)>("updateEventEmitterMountItem");
return updateEventEmitterInstruction(javaUIManager, mutation.newChildShadowView.tag, javaEventEmitter.get());
}
local_ref<JMountItem::javaobject> createUpdatePropsMountItem(const jni::global_ref<jobject> &javaUIManager, const ShadowViewMutation &mutation) {
auto shadowView = mutation.newChildShadowView;
auto newViewProps = *std::dynamic_pointer_cast<const ViewProps>(shadowView.props);
// TODO: move props from map to a typed object.
auto rawProps = shadowView.props->rawProps;
folly::dynamic newProps = folly::dynamic::object();
for (auto element : rawProps) {
newProps[element.first] = element.second;
}
local_ref<ReadableNativeMap::jhybridobject> readableMap = ReadableNativeMap::newObjectCxxArgs(newProps);
static auto updatePropsInstruction =
jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<alias_ref<JMountItem>(jint,ReadableNativeMap::javaobject)>("updatePropsMountItem");
return updatePropsInstruction(javaUIManager,
mutation.newChildShadowView.tag,
readableMap.get());
}
local_ref<JMountItem::javaobject> createUpdateLayoutMountItem(const jni::global_ref<jobject> &javaUIManager, const ShadowViewMutation &mutation) {
auto oldChildShadowView = mutation.oldChildShadowView;
auto newChildShadowView = mutation.newChildShadowView;
if (newChildShadowView.layoutMetrics != EmptyLayoutMetrics && oldChildShadowView.layoutMetrics != newChildShadowView.layoutMetrics) {
static auto updateLayoutInstruction =
jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<alias_ref<JMountItem>(jint, jint, jint, jint, jint)>("updateLayoutMountItem");
auto layoutMetrics = newChildShadowView.layoutMetrics;
auto pointScaleFactor = layoutMetrics.pointScaleFactor;
auto frame = layoutMetrics.frame;
int x = round(frame.origin.x * pointScaleFactor);
int y = round(frame.origin.y * pointScaleFactor);
int w = round(frame.size.width * pointScaleFactor);
int h = round(frame.size.height * pointScaleFactor);
return updateLayoutInstruction(javaUIManager, newChildShadowView.tag, x, y, w, h);
}
return nullptr;
}
local_ref<JMountItem::javaobject> createInsertMountItem(const jni::global_ref<jobject> &javaUIManager, const ShadowViewMutation &mutation) {
static auto insertInstruction =
jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<alias_ref<JMountItem>(jint,jint,jint)>("insertMountItem");
return insertInstruction(javaUIManager, mutation.newChildShadowView.tag, mutation.parentShadowView.tag, mutation.index);
}
local_ref<JMountItem::javaobject> createUpdateLocalData(const jni::global_ref<jobject> &javaUIManager, const ShadowViewMutation &mutation) {
static auto updateLocalDataInstruction =
jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<alias_ref<JMountItem>(jint, ReadableNativeMap::javaobject)>("updateLocalDataMountItem");
auto localData = mutation.newChildShadowView.localData;
folly::dynamic newLocalData = folly::dynamic::object();
if (localData) {
newLocalData = localData->getDynamic();
}
local_ref<ReadableNativeMap::jhybridobject> readableMap = ReadableNativeMap::newObjectCxxArgs(newLocalData);
return updateLocalDataInstruction(javaUIManager, mutation.newChildShadowView.tag, readableMap.get());
}
local_ref<JMountItem::javaobject> createRemoveMountItem(const jni::global_ref<jobject> &javaUIManager, const ShadowViewMutation &mutation) {
static auto removeInstruction =
jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<alias_ref<JMountItem>(jint,jint,jint)>("removeMountItem");
return removeInstruction(javaUIManager, mutation.oldChildShadowView.tag, mutation.parentShadowView.tag, mutation.index);
}
local_ref<JMountItem::javaobject> createDeleteMountItem(const jni::global_ref<jobject> &javaUIManager, const ShadowViewMutation &mutation) {
static auto deleteInstruction =
jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<alias_ref<JMountItem>(jint)>("deleteMountItem");
return deleteInstruction(javaUIManager, mutation.oldChildShadowView.tag);
}
void Binding::schedulerDidFinishTransaction(const Tag rootTag, const ShadowViewMutationList &mutations) {
SystraceSection s("FabricUIManager::schedulerDidFinishTransaction");
std::vector<local_ref<jobject>> queue;
// Upper bound estimation of mount items to be delivered to Java side.
int size = mutations.size() * 3 + 42;
local_ref<JArrayClass<JMountItem::javaobject>> mountItemsArray = JArrayClass<JMountItem::javaobject>::newArray(size);
auto mountItems = *(mountItemsArray);
int position = 0;
for (const auto &mutation : mutations) {
auto oldChildShadowView = mutation.oldChildShadowView;
auto newChildShadowView = mutation.newChildShadowView;
bool isVirtual = newChildShadowView.layoutMetrics == EmptyLayoutMetrics &&
oldChildShadowView.layoutMetrics == EmptyLayoutMetrics;
switch (mutation.type) {
case ShadowViewMutation::Create: {
mountItems[position++] = createCreateMountItem(javaUIManager_, mutation, rootTag);
break;
}
case ShadowViewMutation::Remove: {
if (!isVirtual) {
mountItems[position++] = createRemoveMountItem(javaUIManager_, mutation);
}
break;
}
case ShadowViewMutation::Delete: {
mountItems[position++] = createDeleteMountItem(javaUIManager_, mutation);
break;
}
case ShadowViewMutation::Update: {
if (!isVirtual) {
if (mutation.oldChildShadowView.props != mutation.newChildShadowView.props) {
mountItems[position++] = createUpdatePropsMountItem(javaUIManager_, mutation);
}
if (mutation.oldChildShadowView.localData != mutation.newChildShadowView.localData) {
mountItems[position++] = createUpdateLocalData(javaUIManager_, mutation);
}
auto updateLayoutMountItem = createUpdateLayoutMountItem(javaUIManager_, mutation);
if (updateLayoutMountItem) {
mountItems[position++] = updateLayoutMountItem;
}
}
if (mutation.oldChildShadowView.eventEmitter != mutation.newChildShadowView.eventEmitter) {
auto updateEventEmitterMountItem = createUpdateEventEmitterMountItem(javaUIManager_, mutation);
if (updateEventEmitterMountItem) {
mountItems[position++] = updateEventEmitterMountItem;
}
}
break;
}
case ShadowViewMutation::Insert: {
if (!isVirtual) {
mountItems[position++] = createInsertMountItem(javaUIManager_, mutation);
mountItems[position++] = createUpdatePropsMountItem(javaUIManager_, mutation);
auto updateLayoutMountItem = createUpdateLayoutMountItem(javaUIManager_, mutation);
if (updateLayoutMountItem) {
mountItems[position++] = updateLayoutMountItem;
}
if (mutation.newChildShadowView.localData) {
mountItems[position++] = createUpdateLocalData(javaUIManager_, mutation);
}
}
auto updateEventEmitterMountItem = createUpdateEventEmitterMountItem(javaUIManager_, mutation);
if (updateEventEmitterMountItem) {
mountItems[position++] = updateEventEmitterMountItem;
}
break;
}
default: {
break;
}
}
}
static auto createMountItemsBatchContainer =
jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<alias_ref<JMountItem>(jtypeArray<JMountItem::javaobject>,jint)>("createBatchMountItem");
auto batch = createMountItemsBatchContainer(javaUIManager_, mountItemsArray.get(), position);
static auto scheduleMountItems =
jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<void(JMountItem::javaobject)>("scheduleMountItems");
scheduleMountItems(javaUIManager_, batch.get());
}
void Binding::setPixelDensity(float pointScaleFactor) {
pointScaleFactor_ = pointScaleFactor;
}
void Binding::schedulerDidRequestPreliminaryViewAllocation(const SurfaceId surfaceId, const ComponentName componentName, bool isLayoutable, const ComponentHandle componentHandle) {
if (isLayoutable) {
static auto preallocateView =
jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<void(jint,jstring)>("preallocateView");
preallocateView(javaUIManager_, surfaceId, make_jstring(componentName).get());
}
}
void Binding::registerNatives() {
registerHybrid({
makeNativeMethod("initHybrid", Binding::initHybrid),
makeNativeMethod("installFabricUIManager", Binding::installFabricUIManager),
makeNativeMethod("startSurface", Binding::startSurface),
makeNativeMethod("renderTemplateToSurface", Binding::renderTemplateToSurface),
makeNativeMethod("stopSurface", Binding::stopSurface),
makeNativeMethod("setConstraints", Binding::setConstraints),
makeNativeMethod("setPixelDensity", Binding::setPixelDensity),
makeNativeMethod("uninstallFabricUIManager", Binding::uninstallFabricUIManager)
});
}
}
}

View File

@ -0,0 +1,60 @@
// Copyright 2004-present Facebook. All Rights Reserved.
// 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 "ComponentFactoryDelegate.h"
#include "EventBeatManager.h"
#include <memory>
#include <fb/fbjni.h>
#include <react/jni/JMessageQueueThread.h>
#include <react/jni/ReadableNativeMap.h>
#include <react/uimanager/Scheduler.h>
#include <react/uimanager/SchedulerDelegate.h>
#include <mutex>
namespace facebook {
namespace react {
class Instance;
class Binding : public jni::HybridClass<Binding>, public SchedulerDelegate {
public:
constexpr static const char *const kJavaDescriptor =
"Lcom/facebook/react/fabric/jsi/Binding;";
static void registerNatives();
jni::global_ref<jobject> javaUIManager_;
std::shared_ptr<Scheduler> scheduler_;
float pointScaleFactor_ = 1;
private:
void setConstraints(jint rootTag, jfloat minWidth, jfloat maxWidth, jfloat minHeight, jfloat maxHeight);
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jclass>);
void installFabricUIManager(jlong jsContextNativePointer, jni::alias_ref<jobject> javaUIManager, EventBeatManager* eventBeatManager, jni::alias_ref<JavaMessageQueueThread::javaobject> jsMessageQueueThread, ComponentFactoryDelegate* componentsRegistry);
void startSurface(jint surfaceId, NativeMap *initialProps);
void renderTemplateToSurface(jint surfaceId, jstring uiTemplate);
void stopSurface(jint surfaceId);
void schedulerDidFinishTransaction(const Tag rootTag, const ShadowViewMutationList &mutations);
void schedulerDidRequestPreliminaryViewAllocation(const SurfaceId surfaceId, const ComponentName componentName, bool isLayoutable, const ComponentHandle componentHandle);
void setPixelDensity(float pointScaleFactor);
void uninstallFabricUIManager();
};
}
}

View File

@ -0,0 +1,27 @@
// Copyright 2004-present Facebook. All Rights Reserved.
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
#include "ComponentFactoryDelegate.h"
#include <android/log.h>
#include <react/uimanager/ComponentDescriptorRegistry.h>
#include <fb/fbjni.h>
#include <jsi/jsi.h>
using namespace facebook::jsi;
namespace facebook {
namespace react {
jni::local_ref<ComponentFactoryDelegate::jhybriddata> ComponentFactoryDelegate::initHybrid(
jni::alias_ref<jclass>) {
return makeCxxInstance();
}
void ComponentFactoryDelegate::registerNatives() {
registerHybrid({
makeNativeMethod("initHybrid", ComponentFactoryDelegate::initHybrid),
});
}
}}

View File

@ -0,0 +1,38 @@
// Copyright 2004-present Facebook. All Rights Reserved.
// 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 <react/uimanager/ComponentDescriptorRegistry.h>
#include <react/uimanager/ContextContainer.h>
#include <react/uimanager/Scheduler.h>
#include <fb/fbjni.h>
#include <jsi/jsi.h>
#include <mutex>
#include <unordered_set>
using namespace facebook::jsi;
namespace facebook {
namespace react {
class Instance;
class ComponentFactoryDelegate : public jni::HybridClass<ComponentFactoryDelegate> {
public:
constexpr static const char *const kJavaDescriptor =
"Lcom/facebook/react/fabric/jsi/ComponentFactoryDelegate;";
static void registerNatives();
ComponentRegistryFactory buildRegistryFunction;
private:
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jclass>);
};
}
}

View File

@ -0,0 +1,47 @@
// Copyright 2004-present Facebook. All Rights Reserved.
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
#include "EventBeatManager.h"
#include <fb/fbjni.h>
using namespace facebook::jni;
namespace facebook {
namespace react {
EventBeatManager::EventBeatManager(Runtime* runtime, jni::alias_ref<EventBeatManager::jhybriddata> jhybridobject) : runtime_(runtime), jhybridobject_(jhybridobject) { }
jni::local_ref<EventBeatManager::jhybriddata> EventBeatManager::initHybrid(
jni::alias_ref<EventBeatManager::jhybriddata> jhybridobject, jlong jsContext) {
return makeCxxInstance((Runtime *) jsContext, jhybridobject);
}
void EventBeatManager::registerEventBeat(EventBeat *eventBeat) const {
std::lock_guard<std::mutex> lock(mutex_);
registeredEventBeats_.insert(eventBeat);
}
void EventBeatManager::unregisterEventBeat(EventBeat *eventBeat) const {
std::lock_guard<std::mutex> lock(mutex_);
registeredEventBeats_.erase(eventBeat);
}
void EventBeatManager::beat() {
std::lock_guard<std::mutex> lock(mutex_);
for (const auto eventBeat : registeredEventBeats_) {
eventBeat->beat(*runtime_);
}
}
void EventBeatManager::registerNatives() {
registerHybrid({
makeNativeMethod("initHybrid", EventBeatManager::initHybrid),
makeNativeMethod("beat", EventBeatManager::beat),
});
}
}
}

View File

@ -0,0 +1,50 @@
// Copyright 2004-present Facebook. All Rights Reserved.
// 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 <react/events/EventBeat.h>
#include <fb/fbjni.h>
#include <jsi/jsi.h>
#include <mutex>
#include <unordered_set>
using namespace facebook::jsi;
namespace facebook {
namespace react {
class Instance;
class EventBeatManager : public jni::HybridClass<EventBeatManager> {
public:
constexpr static const char *const kJavaDescriptor =
"Lcom/facebook/react/fabric/jsi/EventBeatManager;";
static void registerNatives();
void registerEventBeat(EventBeat *eventBeat) const;
void unregisterEventBeat(EventBeat *eventBeat) const;
void beat();
EventBeatManager(Runtime* runtime, jni::alias_ref<EventBeatManager::jhybriddata> jhybridobject);
private:
Runtime* runtime_;
jni::alias_ref<EventBeatManager::jhybriddata> jhybridobject_;
mutable std::unordered_set<const EventBeat *> registeredEventBeats_ {}; // Protected by `mutex_`
mutable std::mutex mutex_;
static jni::local_ref<EventBeatManager::jhybriddata> initHybrid(jni::alias_ref<EventBeatManager::jhybriddata> jhybridobject, jlong jsContext);
};
}
}

View File

@ -0,0 +1,30 @@
// Copyright 2004-present Facebook. All Rights Reserved.
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
#include "EventEmitterWrapper.h"
#include <fb/fbjni.h>
using namespace facebook::jni;
namespace facebook {
namespace react {
jni::local_ref<EventEmitterWrapper::jhybriddata> EventEmitterWrapper::initHybrid(
jni::alias_ref<jclass>) {
return makeCxxInstance();
}
void EventEmitterWrapper::invokeEvent(std::string eventName, NativeMap *payload) {
eventEmitter->dispatchEvent(eventName, payload->consume(), EventPriority::AsynchronousBatched);
}
void EventEmitterWrapper::registerNatives() {
registerHybrid({
makeNativeMethod("initHybrid", EventEmitterWrapper::initHybrid),
makeNativeMethod("invokeEvent", EventEmitterWrapper::invokeEvent),
});
}
}
}

View File

@ -0,0 +1,34 @@
// Copyright 2004-present Facebook. All Rights Reserved.
// 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 <fb/fbjni.h>
#include <react/events/EventEmitter.h>
#include <react/jni/ReadableNativeMap.h>
namespace facebook {
namespace react {
class Instance;
class EventEmitterWrapper : public jni::HybridClass<EventEmitterWrapper> {
public:
constexpr static const char *const kJavaDescriptor =
"Lcom/facebook/react/fabric/jsi/EventEmitterWrapper;";
static void registerNatives();
SharedEventEmitter eventEmitter;
void invokeEvent(std::string eventName, NativeMap *params);
private:
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jclass>);
};
}
}

View File

@ -0,0 +1,20 @@
// Copyright 2004-present Facebook. All Rights Reserved.
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
#include <fb/fbjni.h>
#include <fb/xplat_init.h>
#include "Binding.h"
#include "EventBeatManager.h"
#include "EventEmitterWrapper.h"
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
return facebook::xplat::initialize(vm, [] {
facebook::react::Binding::registerNatives();
facebook::react::EventBeatManager::registerNatives();
facebook::react::EventEmitterWrapper::registerNatives();
facebook::react::ComponentFactoryDelegate::registerNatives();
});
}

View File

@ -0,0 +1,53 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* <p>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.mounting;
import android.support.annotation.UiThread;
import android.view.View;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewManagerRegistry;
import java.util.WeakHashMap;
/** Class that provides pool for views based on {@link ThemedReactContext}. */
final class ContextBasedViewPool {
private final WeakHashMap<ThemedReactContext, ViewPool> mContextViewPoolHashMap =
new WeakHashMap<>();
private final ViewManagerRegistry mViewManagerRegistry;
ContextBasedViewPool(ViewManagerRegistry viewManagerRegistry) {
mViewManagerRegistry = viewManagerRegistry;
}
@UiThread
void createView(ThemedReactContext context, String componentName) {
UiThreadUtil.assertOnUiThread();
getViewPool(context).createView(componentName, context);
}
@UiThread
View getOrCreateView(String componentName, ThemedReactContext context) {
UiThreadUtil.assertOnUiThread();
return getViewPool(context).getOrCreateView(componentName, context);
}
@UiThread
void returnToPool(ThemedReactContext context, String componentName, View view) {
UiThreadUtil.assertOnUiThread();
getViewPool(context).returnToPool(componentName, view);
}
@UiThread
private ViewPool getViewPool(ThemedReactContext context) {
ViewPool pool = mContextViewPoolHashMap.get(context);
if (pool == null) {
pool = new ViewPool(mViewManagerRegistry);
mContextViewPoolHashMap.put(context, pool);
}
return pool;
}
}

View File

@ -0,0 +1,57 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* <p>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.mounting;
import static android.view.View.MeasureSpec.EXACTLY;
import android.view.View;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.yoga.YogaMeasureMode;
public class LayoutMetricsConversions {
// Represents the Layout constraint mode "undefined" from React side.
public static final int REACT_CONSTRAINT_UNDEFINED = -2147483648;
public static float getMinSize(int viewMeasureSpec) {
int mode = View.MeasureSpec.getMode(viewMeasureSpec);
int size = View.MeasureSpec.getSize(viewMeasureSpec);
return mode == EXACTLY ? size : 0f;
}
public static float getMaxSize(int viewMeasureSpec) {
int mode = View.MeasureSpec.getMode(viewMeasureSpec);
int size = View.MeasureSpec.getSize(viewMeasureSpec);
return mode == View.MeasureSpec.UNSPECIFIED ? REACT_CONSTRAINT_UNDEFINED : size;
}
public static float getYogaSize(float minSize, float maxSize) {
float yogaSize;
if (minSize == maxSize) {
yogaSize = PixelUtil.toPixelFromDIP(maxSize);
} else if (maxSize == REACT_CONSTRAINT_UNDEFINED) {
yogaSize = 0;
} else {
yogaSize = PixelUtil.toPixelFromDIP(maxSize);
}
return yogaSize;
}
public static YogaMeasureMode getYogaMeasureMode(float minSize, float maxSize) {
YogaMeasureMode yogaMeasureMode;
if (minSize == maxSize) {
yogaMeasureMode = YogaMeasureMode.EXACTLY;
} else if (maxSize == REACT_CONSTRAINT_UNDEFINED) {
yogaMeasureMode = YogaMeasureMode.UNDEFINED;
} else {
yogaMeasureMode = YogaMeasureMode.AT_MOST;
}
return yogaMeasureMode;
}
}

View File

@ -0,0 +1,334 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* <p>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.mounting;
import android.content.Context;
import android.support.annotation.AnyThread;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import com.facebook.react.fabric.FabricUIManager;
import com.facebook.react.fabric.jsi.EventEmitterWrapper;
import com.facebook.react.fabric.mounting.mountitems.MountItem;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableNativeMap;
import com.facebook.react.bridge.SoftAssertions;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.uimanager.IllegalViewOperationException;
import com.facebook.react.uimanager.ReactStylesDiffMap;
import com.facebook.react.uimanager.RootView;
import com.facebook.react.uimanager.RootViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewGroupManager;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.react.uimanager.ViewManagerRegistry;
import com.facebook.react.uimanager.common.SizeMonitoringFrameLayout;
import com.facebook.yoga.YogaMeasureMode;
import java.util.concurrent.ConcurrentHashMap;
/**
* Class responsible for actually dispatching view updates enqueued via {@link
* FabricUIManager#scheduleMountItems(int, MountItem[])} on the UI thread.
*/
public class MountingManager {
private final ConcurrentHashMap<Integer, ViewState> mTagToViewState;
private final ViewManagerRegistry mViewManagerRegistry;
private final RootViewManager mRootViewManager = new RootViewManager();
private final ContextBasedViewPool mViewPool;
public MountingManager(ViewManagerRegistry viewManagerRegistry) {
mTagToViewState = new ConcurrentHashMap<>();
mViewManagerRegistry = viewManagerRegistry;
mViewPool = new ContextBasedViewPool(viewManagerRegistry);
}
@UiThread
public void addRootView(int reactRootTag, SizeMonitoringFrameLayout rootView) {
if (rootView.getId() != View.NO_ID) {
throw new IllegalViewOperationException(
"Trying to add a root view with an explicit id already set. React Native uses "
+ "the id field to track react tags and will overwrite this field. If that is fine, "
+ "explicitly overwrite the id field to View.NO_ID before calling addRootView.");
}
mTagToViewState.put(
reactRootTag, new ViewState(reactRootTag, rootView, mRootViewManager, true));
rootView.setId(reactRootTag);
}
/** Releases all references to given native View. */
@UiThread
private void dropView(View view) {
UiThreadUtil.assertOnUiThread();
int reactTag = view.getId();
ViewState state = getViewState(reactTag);
ViewManager viewManager = state.mViewManager;
if (!state.mIsRoot && viewManager != null) {
// For non-root views we notify viewmanager with {@link ViewManager#onDropInstance}
viewManager.onDropViewInstance(view);
}
if (view instanceof ViewGroup && viewManager instanceof ViewGroupManager) {
ViewGroup viewGroup = (ViewGroup) view;
ViewGroupManager<ViewGroup> viewGroupManager = getViewGroupManager(state);
for (int i = viewGroupManager.getChildCount(viewGroup) - 1; i >= 0; i--) {
View child = viewGroupManager.getChildAt(viewGroup, i);
if (mTagToViewState.get(child.getId()) != null) {
dropView(child);
}
viewGroupManager.removeViewAt(viewGroup, i);
}
}
mTagToViewState.remove(reactTag);
Context context = view.getContext();
mViewPool.returnToPool(
(ThemedReactContext) context, Assertions.assertNotNull(viewManager).getName(), view);
}
/** Releases all references to react root tag. */
@UiThread
public void removeRootView(int reactRootTag) {
UiThreadUtil.assertOnUiThread();
ViewState viewState = mTagToViewState.get(reactRootTag);
if (viewState == null || !viewState.mIsRoot) {
SoftAssertions.assertUnreachable(
"View with tag " + reactRootTag + " is not registered as a root view");
}
if (viewState.mView != null) {
dropView(viewState.mView);
}
}
@UiThread
public void addViewAt(int parentTag, int tag, int index) {
UiThreadUtil.assertOnUiThread();
ViewState parentViewState = getViewState(parentTag);
final ViewGroup parentView = (ViewGroup) parentViewState.mView;
final View view = getViewState(tag).mView;
if (view == null) {
throw new IllegalStateException("Unable to find view for tag " + tag);
}
getViewGroupManager(parentViewState).addView(parentView, view, index);
}
private ViewState getViewState(int tag) {
ViewState viewState = mTagToViewState.get(tag);
if (viewState == null) {
throw new IllegalStateException("Unable to find viewState for tag " + tag);
}
return viewState;
}
public void receiveCommand(int reactTag, int commandId, @Nullable ReadableArray commandArgs) {
ViewState viewState = getViewState(reactTag);
if (viewState.mViewManager == null) {
throw new IllegalStateException("Unable to find viewState manager for tag " + reactTag);
}
if (viewState.mView == null) {
throw new IllegalStateException("Unable to find viewState view for tag " + reactTag);
}
viewState.mViewManager.receiveCommand(viewState.mView, commandId, commandArgs);
}
@SuppressWarnings("unchecked") // prevents unchecked conversion warn of the <ViewGroup> type
private static ViewGroupManager<ViewGroup> getViewGroupManager(ViewState viewState) {
if (viewState.mViewManager == null) {
throw new IllegalStateException("Unable to find ViewManager");
}
return (ViewGroupManager<ViewGroup>) viewState.mViewManager;
}
@UiThread
public void removeViewAt(int parentTag, int index) {
UiThreadUtil.assertOnUiThread();
ViewState viewState = getViewState(parentTag);
final ViewGroup parentView = (ViewGroup) viewState.mView;
if (parentView == null) {
throw new IllegalStateException("Unable to find view for tag " + parentTag);
}
getViewGroupManager(viewState).removeViewAt(parentView, index);
}
@UiThread
public void createView(
ThemedReactContext themedReactContext,
String componentName,
int reactTag,
boolean isVirtual) {
UiThreadUtil.assertOnUiThread();
View view = null;
ViewManager viewManager = null;
if (!isVirtual) {
viewManager = mViewManagerRegistry.get(componentName);
view = mViewPool.getOrCreateView(componentName, themedReactContext);
view.setId(reactTag);
}
mTagToViewState.put(reactTag, new ViewState(reactTag, view, viewManager));
}
@UiThread
public void updateProps(int reactTag, ReadableMap props) {
if (props == null) {
return;
}
UiThreadUtil.assertOnUiThread();
ViewState viewState = getViewState(reactTag);
viewState.mCurrentProps = new ReactStylesDiffMap(props);
View view = viewState.mView;
if (view == null) {
throw new IllegalStateException("Unable to find view for tag " + reactTag);
}
Assertions.assertNotNull(viewState.mViewManager)
.updateProperties(view, viewState.mCurrentProps);
}
@UiThread
public void updateLayout(int reactTag, int x, int y, int width, int height) {
UiThreadUtil.assertOnUiThread();
ViewState viewState = getViewState(reactTag);
// Do not layout Root Views
if (viewState.mIsRoot) {
return;
}
View viewToUpdate = viewState.mView;
if (viewToUpdate == null) {
throw new IllegalStateException("Unable to find View for tag: " + reactTag);
}
viewToUpdate.measure(
View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));
ViewParent parent = viewToUpdate.getParent();
if (parent instanceof RootView) {
parent.requestLayout();
}
// TODO: T31905686 Check if the parent of the view has to layout the view, or the child has
// to lay itself out. see NativeViewHierarchyManager.updateLayout
viewToUpdate.layout(x, y, x + width, y + height);
}
@UiThread
public void deleteView(int reactTag) {
UiThreadUtil.assertOnUiThread();
View view = getViewState(reactTag).mView;
if (view != null) {
dropView(view);
} else {
mTagToViewState.remove(reactTag);
}
}
@UiThread
public void updateLocalData(int reactTag, ReadableMap newLocalData) {
UiThreadUtil.assertOnUiThread();
ViewState viewState = getViewState(reactTag);
if (viewState.mCurrentProps == null) {
throw new IllegalStateException(
"Can not update local data to view without props: " + reactTag);
}
if (viewState.mCurrentLocalData != null
&& newLocalData.hasKey("hash")
&& viewState.mCurrentLocalData.getDouble("hash") == newLocalData.getDouble("hash")
&& viewState.mCurrentLocalData.toString().equals(newLocalData.toString())) {
// TODO: T31905686 implement a proper equality method
return;
}
viewState.mCurrentLocalData = newLocalData;
ViewManager viewManager = viewState.mViewManager;
if (viewManager == null) {
throw new IllegalStateException("Unable to find ViewManager for tag: " + reactTag);
}
Object extraData =
viewManager.updateLocalData(
viewState.mView,
viewState.mCurrentProps,
new ReactStylesDiffMap(viewState.mCurrentLocalData));
if (extraData != null) {
viewManager.updateExtraData(viewState.mView, extraData);
}
}
@UiThread
public void preallocateView(ThemedReactContext reactContext, String componentName) {
mViewPool.createView(reactContext, componentName);
}
@UiThread
public void updateEventEmitter(int reactTag, EventEmitterWrapper eventEmitter) {
UiThreadUtil.assertOnUiThread();
ViewState viewState = getViewState(reactTag);
viewState.mEventEmitter = eventEmitter;
}
@AnyThread
public long measure(
ReactContext context,
String componentName,
ReadableNativeMap localData,
ReadableNativeMap props,
float width,
YogaMeasureMode widthMode,
float height,
YogaMeasureMode heightMode) {
return mViewManagerRegistry
.get(componentName)
.measure(context, localData, props, width, widthMode, height, heightMode);
}
@AnyThread
public @Nullable EventEmitterWrapper getEventEmitter(int reactTag) {
ViewState viewState = mTagToViewState.get(reactTag);
return viewState == null ? null : viewState.mEventEmitter;
}
/**
* This class holds view state for react tags. Objects of this class are stored into the {@link
* #mTagToViewState}, and they should be updated in the same thread.
*/
private static class ViewState {
@Nullable final View mView;
final int mReactTag;
final boolean mIsRoot;
@Nullable final ViewManager mViewManager;
public ReactStylesDiffMap mCurrentProps;
public ReadableMap mCurrentLocalData;
public EventEmitterWrapper mEventEmitter;
private ViewState(int reactTag, @Nullable View view, @Nullable ViewManager viewManager) {
this(reactTag, view, viewManager, false);
}
private ViewState(int reactTag, @Nullable View view, ViewManager viewManager, boolean isRoot) {
mReactTag = reactTag;
mView = view;
mIsRoot = isRoot;
mViewManager = viewManager;
}
}
}

View File

@ -0,0 +1,63 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* <p>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.mounting;
import android.support.annotation.UiThread;
import android.view.View;
import com.facebook.react.common.ClearableSynchronizedPool;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.react.uimanager.ViewManagerRegistry;
import java.util.HashMap;
import java.util.Map;
final class ViewPool {
private static final int POOL_SIZE = 512;
private final Map<String, ClearableSynchronizedPool<View>> mViewPool = new HashMap<>();
private final ViewManagerRegistry mViewManagerRegistry;
ViewPool(ViewManagerRegistry viewManagerRegistry) {
mViewManagerRegistry = viewManagerRegistry;
}
@UiThread
void createView(String componentName, ThemedReactContext context) {
ClearableSynchronizedPool<View> viewPool = getViewPoolForComponent(componentName);
ViewManager viewManager = mViewManagerRegistry.get(componentName);
// TODO: T31905686 Integrate / re-implement jsResponder
View view = viewManager.createView(context, null);
viewPool.release(view);
}
@UiThread
View getOrCreateView(String componentName, ThemedReactContext context) {
ClearableSynchronizedPool<View> viewPool = getViewPoolForComponent(componentName);
View view = viewPool.acquire();
if (view == null) {
createView(componentName, context);
view = viewPool.acquire();
}
return view;
}
@UiThread
void returnToPool(String componentName, View view) {
ClearableSynchronizedPool<View> viewPool = mViewPool.get(componentName);
if (viewPool != null) {
viewPool.release(view);
}
}
private ClearableSynchronizedPool<View> getViewPoolForComponent(String componentName) {
ClearableSynchronizedPool<View> viewPool = mViewPool.get(componentName);
if (viewPool == null) {
viewPool = new ClearableSynchronizedPool<>(POOL_SIZE);
mViewPool.put(componentName, viewPool);
}
return viewPool;
}
}

View File

@ -0,0 +1,52 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* <p>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.mounting.mountitems;
import com.facebook.react.fabric.mounting.MountingManager;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.systrace.Systrace;
/**
* This class represents a batch of {@link MountItem}s
*
* <p>A MountItem batch contains an array of {@link MountItem} and a size. The size determines the
* amount of items that needs to be processed in the array.
*
* <p>The purpose of encapsulating the array of MountItems this way, is to reduce the amount of
* allocations in C++
*/
@DoNotStrip
public class BatchMountItem implements MountItem {
private final MountItem[] mMountItems;
private final int mSize;
public BatchMountItem(MountItem[] items, int size) {
if (items == null) {
throw new NullPointerException();
}
if (size < 0 || size > items.length) {
throw new IllegalArgumentException(
"Invalid size received by parameter size: " + size + " items.size = " + items.length);
}
mMountItems = items;
mSize = size;
}
@Override
public void execute(MountingManager mountingManager) {
Systrace.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "FabricUIManager::mountViews (" + mSize + " items)");
for (int mountItemIndex = 0; mountItemIndex < mSize; mountItemIndex++) {
MountItem mountItem = mMountItems[mountItemIndex];
mountItem.execute(mountingManager);
}
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}

View File

@ -0,0 +1,47 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* <p>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.mounting.mountitems;
import com.facebook.react.fabric.mounting.MountingManager;
import com.facebook.react.uimanager.ThemedReactContext;
public class CreateMountItem implements MountItem {
private final String mComponentName;
private final int mReactTag;
private final ThemedReactContext mThemedReactContext;
private final boolean mIsVirtual;
public CreateMountItem(
ThemedReactContext themedReactContext,
String componentName,
int reactTag,
boolean isVirtual) {
mReactTag = reactTag;
mThemedReactContext = themedReactContext;
mComponentName = componentName;
mIsVirtual = isVirtual;
}
@Override
public void execute(MountingManager mountingManager) {
mountingManager.createView(mThemedReactContext, mComponentName, mReactTag, mIsVirtual);
}
public String getComponentName() {
return mComponentName;
}
public ThemedReactContext getThemedReactContext() {
return mThemedReactContext;
}
@Override
public String toString() {
return "CreateMountItem [" + mReactTag + "] " + mComponentName;
}
}

View File

@ -0,0 +1,28 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* <p>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.mounting.mountitems;
import com.facebook.react.fabric.mounting.MountingManager;
public class DeleteMountItem implements MountItem {
private int mReactTag;
public DeleteMountItem(int reactTag) {
mReactTag = reactTag;
}
@Override
public void execute(MountingManager mountingManager) {
mountingManager.deleteView(mReactTag);
}
@Override
public String toString() {
return "DeleteMountItem [" + mReactTag + "]";
}
}

View File

@ -0,0 +1,37 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* <p>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.mounting.mountitems;
import android.support.annotation.Nullable;
import com.facebook.react.fabric.mounting.MountingManager;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.UiThreadUtil;
public class DispatchCommandMountItem implements MountItem {
private final int mReactTag;
private final int mCommandId;
private final @Nullable ReadableArray mCommandArgs;
public DispatchCommandMountItem(
int reactTag, int commandId, @Nullable ReadableArray commandArgs) {
mReactTag = reactTag;
mCommandId = commandId;
mCommandArgs = commandArgs;
}
@Override
public void execute(MountingManager mountingManager) {
UiThreadUtil.assertOnUiThread();
mountingManager.receiveCommand(mReactTag, mCommandId, mCommandArgs);
}
@Override
public String toString() {
return "DispatchCommandMountItem [" + mReactTag + "] " + mCommandId;
}
}

View File

@ -0,0 +1,45 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* <p>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.mounting.mountitems;
import com.facebook.react.fabric.mounting.MountingManager;
public class InsertMountItem implements MountItem {
private int mReactTag;
private int mParentReactTag;
private int mIndex;
public InsertMountItem(int reactTag, int parentReactTag, int index) {
mReactTag = reactTag;
mParentReactTag = parentReactTag;
mIndex = index;
}
@Override
public void execute(MountingManager mountingManager) {
mountingManager.addViewAt(mParentReactTag, mReactTag, mIndex);
}
public int getParentReactTag() {
return mParentReactTag;
}
public int getIndex() {
return mIndex;
}
@Override
public String toString() {
return "InsertMountItem ["
+ mReactTag
+ "] - parentTag: "
+ mParentReactTag
+ " - index: "
+ mIndex;
}
}

View File

@ -0,0 +1,17 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* <p>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.mounting.mountitems;
import android.support.annotation.UiThread;
import com.facebook.react.fabric.mounting.MountingManager;
public interface MountItem {
/** Execute this {@link MountItem} into the operation queue received by parameter. */
@UiThread
void execute(MountingManager mountingManager);
}

View File

@ -0,0 +1,45 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* <p>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.mounting.mountitems;
import com.facebook.react.fabric.mounting.MountingManager;
public class RemoveMountItem implements MountItem {
private int mReactTag;
private int mParentReactTag;
private int mIndex;
public RemoveMountItem(int reactTag, int parentReactTag, int index) {
mReactTag = reactTag;
mParentReactTag = parentReactTag;
mIndex = index;
}
@Override
public void execute(MountingManager mountingManager) {
mountingManager.removeViewAt(mParentReactTag, mIndex);
}
public int getParentReactTag() {
return mParentReactTag;
}
public int getIndex() {
return mIndex;
}
@Override
public String toString() {
return "RemoveMountItem ["
+ mReactTag
+ "] - parentTag: "
+ mParentReactTag
+ " - index: "
+ mIndex;
}
}

View File

@ -0,0 +1,31 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* <p>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.mounting.mountitems;
import com.facebook.react.fabric.jsi.EventEmitterWrapper;
import com.facebook.react.fabric.mounting.MountingManager;
public class UpdateEventEmitterMountItem implements MountItem {
private final EventEmitterWrapper mEventHandler;
private final int mReactTag;
public UpdateEventEmitterMountItem(int reactTag, EventEmitterWrapper EventHandler) {
mReactTag = reactTag;
mEventHandler = EventHandler;
}
@Override
public void execute(MountingManager mountingManager) {
mountingManager.updateEventEmitter(mReactTag, mEventHandler);
}
@Override
public String toString() {
return "UpdateEventEmitterMountItem [" + mReactTag + "]";
}
}

View File

@ -0,0 +1,61 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* <p>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.mounting.mountitems;
import com.facebook.react.fabric.mounting.MountingManager;
public class UpdateLayoutMountItem implements MountItem {
private final int mReactTag;
private final int mX;
private final int mY;
private final int mWidth;
private final int mHeight;
public UpdateLayoutMountItem(int reactTag, int x, int y, int width, int height) {
mReactTag = reactTag;
mX = x;
mY = y;
mWidth = width;
mHeight = height;
}
@Override
public void execute(MountingManager mountingManager) {
mountingManager.updateLayout(mReactTag, mX, mY, mWidth, mHeight);
}
public int getX() {
return mX;
}
public int getY() {
return mY;
}
public int getHeight() {
return mHeight;
}
public int getWidth() {
return mWidth;
}
@Override
public String toString() {
return "UpdateLayoutMountItem ["
+ mReactTag
+ "] - x: "
+ mX
+ " - y: "
+ mY
+ " - height: "
+ mHeight
+ " - width: "
+ mWidth;
}
}

View File

@ -0,0 +1,36 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* <p>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.mounting.mountitems;
import com.facebook.react.fabric.mounting.MountingManager;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableNativeMap;
public class UpdateLocalDataMountItem implements MountItem {
private final int mReactTag;
private final ReadableMap mNewLocalData;
public UpdateLocalDataMountItem(int reactTag, ReadableNativeMap newLocalData) {
mReactTag = reactTag;
mNewLocalData = newLocalData;
}
@Override
public void execute(MountingManager mountingManager) {
mountingManager.updateLocalData(mReactTag, mNewLocalData);
}
public ReadableMap getNewLocalData() {
return mNewLocalData;
}
@Override
public String toString() {
return "UpdateLocalDataMountItem [" + mReactTag + "] - localData: " + mNewLocalData;
}
}

View File

@ -0,0 +1,31 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* <p>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.mounting.mountitems;
import com.facebook.react.fabric.mounting.MountingManager;
import com.facebook.react.bridge.ReadableMap;
public class UpdatePropsMountItem implements MountItem {
private final int mReactTag;
private final ReadableMap mUpdatedProps;
public UpdatePropsMountItem(int reactTag, ReadableMap updatedProps) {
mReactTag = reactTag;
mUpdatedProps = updatedProps;
}
@Override
public void execute(MountingManager mountingManager) {
mountingManager.updateProps(mReactTag, mUpdatedProps);
}
@Override
public String toString() {
return "UpdatePropsMountItem [" + mReactTag + "] - props: " + mUpdatedProps;
}
}

View File

@ -15,11 +15,12 @@ namespace react {
/**
* This is a sample implementation. Each app should provide its own.
*/
SharedComponentDescriptorRegistry ComponentDescriptorFactory::buildRegistry(
const SharedEventDispatcher &eventDispatcher,
const SharedContextContainer &contextContainer) {
auto registry = std::make_shared<ComponentDescriptorRegistry>();
return registry;
ComponentRegistryFactory getDefaultComponentRegistryFactory() {
return [](const SharedEventDispatcher &eventDispatcher,
const SharedContextContainer &contextContainer) {
auto registry = std::make_shared<ComponentDescriptorRegistry>();
return registry;
}
}
} // namespace react

View File

@ -31,7 +31,7 @@ Size TextLayoutManager::measure(
"FabricUIManager");
static auto measure =
jni::findClassStatic("com/facebook/fbreact/fabric/FabricUIManager")
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<jlong(
jstring,
ReadableNativeMap::javaobject,

View File

@ -23,12 +23,12 @@ namespace react {
* Each app must provide an implementation of the static class method which
* should register its specific set of supported components.
*/
class ComponentDescriptorFactory {
public:
static SharedComponentDescriptorRegistry buildRegistry(
const SharedEventDispatcher &eventDispatcher,
const SharedContextContainer &contextContainer);
};
using ComponentRegistryFactory =
std::function<SharedComponentDescriptorRegistry(
const SharedEventDispatcher &eventDispatcher,
const SharedContextContainer &contextContainer)>;
ComponentRegistryFactory getDefaultComponentRegistryFactory();
} // namespace react
} // namespace facebook

View File

@ -13,12 +13,12 @@
#include <react/uimanager/UIManagerBinding.h>
#include <react/uimanager/UITemplateProcessor.h>
#include "ComponentDescriptorFactory.h"
namespace facebook {
namespace react {
Scheduler::Scheduler(const SharedContextContainer &contextContainer) {
Scheduler::Scheduler(
const SharedContextContainer &contextContainer,
ComponentRegistryFactory buildRegistryFunction) {
const auto asynchronousEventBeatFactory =
contextContainer->getInstance<EventBeatFactory>("asynchronous");
const auto synchronousEventBeatFactory =
@ -46,8 +46,8 @@ Scheduler::Scheduler(const SharedContextContainer &contextContainer) {
auto eventDispatcher = std::make_shared<EventDispatcher>(
eventPipe, synchronousEventBeatFactory, asynchronousEventBeatFactory);
componentDescriptorRegistry_ = ComponentDescriptorFactory::buildRegistry(
eventDispatcher, contextContainer);
componentDescriptorRegistry_ =
buildRegistryFunction(eventDispatcher, contextContainer);
uiManagerRef.setDelegate(this);
uiManagerRef.setShadowTreeRegistry(&shadowTreeRegistry_);

View File

@ -11,6 +11,7 @@
#include <react/config/ReactNativeConfig.h>
#include <react/core/ComponentDescriptor.h>
#include <react/core/LayoutConstraints.h>
#include <react/uimanager/ComponentDescriptorFactory.h>
#include <react/uimanager/ComponentDescriptorRegistry.h>
#include <react/uimanager/ContextContainer.h>
#include <react/uimanager/SchedulerDelegate.h>
@ -29,7 +30,9 @@ namespace react {
*/
class Scheduler final : public UIManagerDelegate, public ShadowTreeDelegate {
public:
Scheduler(const SharedContextContainer &contextContainer);
Scheduler(
const SharedContextContainer &contextContainer,
ComponentRegistryFactory buildRegistryFunction);
~Scheduler();
#pragma mark - Surface Management

View File

@ -21,33 +21,37 @@ using namespace facebook::react;
#include <react/components/text/TextComponentDescriptor.h>
#include <react/components/view/ViewComponentDescriptor.h>
#include <react/config/ReactNativeConfig.h>
#include <react/uimanager/ComponentDescriptorFactory.h>
#include <react/uimanager/ComponentDescriptorRegistry.h>
#include <react/uimanager/ContextContainer.h>
namespace facebook {
namespace react {
SharedComponentDescriptorRegistry ComponentDescriptorFactory::buildRegistry(
const SharedEventDispatcher &eventDispatcher,
const SharedContextContainer &contextContainer) {
auto registry = std::make_shared<ComponentDescriptorRegistry>();
registry->registerComponentDescriptor(
std::make_shared<ViewComponentDescriptor>(eventDispatcher));
registry->registerComponentDescriptor(
std::make_shared<ImageComponentDescriptor>(
eventDispatcher, contextContainer));
registry->registerComponentDescriptor(
std::make_shared<ScrollViewComponentDescriptor>(eventDispatcher));
registry->registerComponentDescriptor(
std::make_shared<ParagraphComponentDescriptor>(
eventDispatcher, contextContainer));
registry->registerComponentDescriptor(
std::make_shared<TextComponentDescriptor>(eventDispatcher));
registry->registerComponentDescriptor(
std::make_shared<RawTextComponentDescriptor>(eventDispatcher));
registry->registerComponentDescriptor(
std::make_shared<ActivityIndicatorViewComponentDescriptor>(
eventDispatcher));
return registry;
// TODO (T29441913): Codegen this app-specific implementation.
ComponentRegistryFactory getDefaultComponentRegistryFactory() {
return [](const SharedEventDispatcher &eventDispatcher,
const SharedContextContainer &contextContainer) {
auto registry = std::make_shared<ComponentDescriptorRegistry>();
registry->registerComponentDescriptor(
std::make_shared<ViewComponentDescriptor>(eventDispatcher));
registry->registerComponentDescriptor(
std::make_shared<ImageComponentDescriptor>(
eventDispatcher, contextContainer));
registry->registerComponentDescriptor(
std::make_shared<ScrollViewComponentDescriptor>(eventDispatcher));
registry->registerComponentDescriptor(
std::make_shared<ParagraphComponentDescriptor>(
eventDispatcher, contextContainer));
registry->registerComponentDescriptor(
std::make_shared<TextComponentDescriptor>(eventDispatcher));
registry->registerComponentDescriptor(
std::make_shared<RawTextComponentDescriptor>(eventDispatcher));
registry->registerComponentDescriptor(
std::make_shared<ActivityIndicatorViewComponentDescriptor>(
eventDispatcher));
return registry;
};
}
bool mockSimpleTestValue_;
@ -93,7 +97,7 @@ std::shared_ptr<const ReactNativeConfig> mockReactNativeConfig_ =
TEST(UITemplateProcessorTest, testSimpleBytecode) {
auto surfaceId = 11;
auto componentDescriptorRegistry =
ComponentDescriptorFactory::buildRegistry(nullptr, nullptr);
getDefaultComponentRegistryFactory()(nullptr, nullptr);
auto nativeModuleRegistry = buildNativeModuleRegistry();
auto bytecode = R"delim({"version":0.1,"commands":[
@ -127,7 +131,7 @@ TEST(UITemplateProcessorTest, testSimpleBytecode) {
TEST(UITemplateProcessorTest, testConditionalBytecode) {
auto surfaceId = 11;
auto componentDescriptorRegistry =
ComponentDescriptorFactory::buildRegistry(nullptr, nullptr);
getDefaultComponentRegistryFactory()(nullptr, nullptr);
auto nativeModuleRegistry = buildNativeModuleRegistry();
auto bytecode = R"delim({"version":0.1,"commands":[