mirror of
https://github.com/status-im/react-native.git
synced 2025-02-23 14:48:25 +00:00
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:
parent
cfbb2278a0
commit
b421b5f4bd
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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 = [
|
||||
],
|
||||
)
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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_);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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,
|
||||
],
|
||||
)
|
@ -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)
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -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),
|
||||
});
|
||||
}
|
||||
|
||||
}}
|
@ -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>);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -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),
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -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),
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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>);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -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();
|
||||
});
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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 + "]";
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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 + "]";
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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_);
|
||||
|
@ -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
|
||||
|
@ -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":[
|
||||
|
Loading…
x
Reference in New Issue
Block a user