Android plumbing for State and LocalData update mount items

Summary: Android plumbing for State and LocalData update mount items. See other commits in stack for usage

Reviewed By: mdvacca

Differential Revision: D14663522

fbshipit-source-id: 5604a6a9af292805e9ce46c68e5ce7472eef0218
This commit is contained in:
Joshua Gross
2019-04-06 00:49:29 -07:00
committed by Facebook Github Bot
parent 4018e799ba
commit e34761ff25
15 changed files with 348 additions and 10 deletions

View File

@@ -12,6 +12,7 @@ import com.facebook.react.fabric.jsi.ComponentRegistry;
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.jsi.StateWrapperImpl;
import com.facebook.react.fabric.mounting.ContextBasedViewPool;
import com.facebook.react.fabric.mounting.LayoutMetricsConversions;
import com.facebook.react.fabric.mounting.MountingManager;
@@ -27,6 +28,7 @@ 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.react.uimanager.StateWrapper;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.systrace.Systrace;
@@ -111,6 +113,7 @@ public class FabricJSIModuleProvider implements JSIModuleProvider<UIManager> {
ComponentRegistry.class.getClass();
EventBeatManager.class.getClass();
EventEmitterWrapper.class.getClass();
StateWrapperImpl.class.getClass();
FabricSoLoader.class.getClass();
PreAllocateViewMountItem.class.getClass();
}

View File

@@ -38,6 +38,7 @@ 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.jsi.StateWrapperImpl;
import com.facebook.react.fabric.mounting.MountingManager;
import com.facebook.react.fabric.mounting.mountitems.BatchMountItem;
import com.facebook.react.fabric.mounting.mountitems.DeleteMountItem;
@@ -50,8 +51,10 @@ 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.react.fabric.mounting.mountitems.UpdateStateMountItem;
import com.facebook.react.modules.core.ReactChoreographer;
import com.facebook.react.uimanager.ReactRootViewTagGenerator;
import com.facebook.react.uimanager.StateWrapper;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewManagerPropertyUpdater;
import com.facebook.react.uimanager.ViewManagerRegistry;
@@ -236,6 +239,12 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
return new UpdateLocalDataMountItem(reactTag, newLocalData);
}
@DoNotStrip
@SuppressWarnings("unused")
private MountItem updateStateMountItem(int reactTag, Object stateWrapper) {
return new UpdateStateMountItem(reactTag, (StateWrapper) stateWrapper);
}
@DoNotStrip
@SuppressWarnings("unused")
private MountItem updateEventEmitterMountItem(int reactTag, Object eventEmitter) {

View File

@@ -0,0 +1,41 @@
/**
* 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.NativeMap;
import com.facebook.react.bridge.ReadableNativeMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.StateWrapper;
/**
* 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 StateWrapperImpl implements StateWrapper {
static {
FabricSoLoader.staticInit();
}
@DoNotStrip private final HybridData mHybridData;
private static native HybridData initHybrid();
private StateWrapperImpl() {
mHybridData = initHybrid();
}
@Override public native ReadableNativeMap getState();
public native void updateStateImpl(NativeMap map);
@Override public void updateState(WritableMap map) {
updateStateImpl((NativeMap)map);
}
}

View File

@@ -6,6 +6,7 @@
#include "AsyncEventBeat.h"
#include "EventEmitterWrapper.h"
#include "ReactNativeConfigHolder.h"
#include "StateWrapperImpl.h"
#include <fb/fbjni.h>
#include <jsi/JSIDynamic.h>
@@ -276,6 +277,32 @@ local_ref<JMountItem::javaobject> createUpdateLocalData(
castReadableMap(readableNativeMap).get());
}
local_ref<JMountItem::javaobject> createUpdateStateMountItem(
const jni::global_ref<jobject>& javaUIManager,
const ShadowViewMutation& mutation) {
static auto updateStateInstruction =
jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<alias_ref<JMountItem>(jint, jobject)>(
"updateStateMountItem");
auto state = mutation.newChildShadowView.state;
// We use state.get() to pass a raw pointer through the JNI
// We don't need to access the state ptr in Java, but we need to be able to
// pass a state object back through the JNI for state updates
// Do not hold onto Java object from C
auto javaStateWrapper = StateWrapperImpl::newObjectJavaArgs();
StateWrapperImpl* cStateWrapper = cthis(javaStateWrapper);
cStateWrapper->state_ = state.get();
return updateStateInstruction(
javaUIManager,
mutation.newChildShadowView.tag,
javaStateWrapper.get());
}
local_ref<JMountItem::javaobject> createRemoveMountItem(
const jni::global_ref<jobject>& javaUIManager,
const ShadowViewMutation& mutation) {
@@ -351,6 +378,11 @@ void Binding::schedulerDidFinishTransaction(
mountItems[position++] =
createUpdateLocalData(javaUIManager_, mutation);
}
if (mutation.oldChildShadowView.state !=
mutation.newChildShadowView.state) {
mountItems[position++] =
createUpdateStateMountItem(javaUIManager_, mutation);
}
auto updateLayoutMountItem =
createUpdateLayoutMountItem(javaUIManager_, mutation);
@@ -371,31 +403,42 @@ void Binding::schedulerDidFinishTransaction(
}
case ShadowViewMutation::Insert: {
if (!isVirtual) {
mountItems[position++] =
createInsertMountItem(javaUIManager_, mutation);
// Insert item
mountItems[position++] = createInsertMountItem(javaUIManager_, mutation);
// Props
if (mutation.newChildShadowView.props->revision > 1) {
mountItems[position++] =
createUpdatePropsMountItem(javaUIManager_, mutation);
}
// LocalData
if (mutation.newChildShadowView.localData) {
mountItems[position++] =
createUpdateLocalData(javaUIManager_, mutation);
}
// Layout
auto updateLayoutMountItem =
createUpdateLayoutMountItem(javaUIManager_, mutation);
if (updateLayoutMountItem) {
mountItems[position++] = updateLayoutMountItem;
}
if (mutation.newChildShadowView.localData) {
// State
if (mutation.newChildShadowView.state) {
mountItems[position++] =
createUpdateLocalData(javaUIManager_, mutation);
createUpdateStateMountItem(javaUIManager_, mutation);
}
}
// EventEmitter
auto updateEventEmitterMountItem =
createUpdateEventEmitterMountItem(javaUIManager_, mutation);
if (updateEventEmitterMountItem) {
mountItems[position++] = updateEventEmitterMountItem;
}
break;
}
default: {

View File

@@ -0,0 +1,41 @@
// 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 "NodeStateWrapper.h"
#include <fb/fbjni.h>
#include <react/jni/ReadableNativeMap.h>
using namespace facebook::jni;
namespace facebook {
namespace react {
jni::local_ref<NodeStateWrapper::jhybriddata>
NodeStateWrapper::initHybrid(jni::alias_ref<jclass>) {
return makeCxxInstance();
}
jni::local_ref<ReadableNativeMap::jhybridobject> NodeStateWrapper::getState() {
folly::dynamic map = state_->getDynamic();
local_ref<ReadableNativeMap::jhybridobject> readableNativeMap =
ReadableNativeMap::newObjectCxxArgs(map);
return readableNativeMap;
}
void NodeStateWrapper::updateState(ReadableNativeMap* map) {
// Get folly::dynamic from map
auto dynamicMap = map->consume();
// Set state
state_->updateState(dynamicMap);
}
void NodeStateWrapper::registerNatives() {
registerHybrid({
makeNativeMethod("getState", NodeStateWrapper::getState),
makeNativeMethod("updateState", NodeStateWrapper::updateState),
});
}
} // namespace react
} // namespace facebook

View File

@@ -0,0 +1,32 @@
// 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/core/State.h>
#include <react/jni/ReadableNativeMap.h>
namespace facebook {
namespace react {
class NodeStateWrapper : public jni::HybridClass<NodeStateWrapper> {
public:
constexpr static const char* const kJavaDescriptor =
"Lcom/facebook/react/fabric/NodeStateWrapper;";
NodeStateWrapper() {}
static void registerNatives();
jni::local_ref<ReadableNativeMap::jhybridobject> getState();
void updateState(ReadableNativeMap* map);
const State* state_;
private:
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jclass>);
};
} // namespace react
} // namespace facebook

View File

@@ -9,12 +9,14 @@
#include "Binding.h"
#include "EventBeatManager.h"
#include "EventEmitterWrapper.h"
#include "StateWrapperImpl.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::StateWrapperImpl::registerNatives();
facebook::react::ComponentFactoryDelegate::registerNatives();
});
}

View File

@@ -0,0 +1,45 @@
// 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 "StateWrapperImpl.h"
#include <fb/fbjni.h>
#include <react/jni/ReadableNativeMap.h>
using namespace facebook::jni;
namespace facebook {
namespace react {
/**
* Called from Java constructor through the JNI.
*/
jni::local_ref<StateWrapperImpl::jhybriddata>
StateWrapperImpl::initHybrid(jni::alias_ref<jclass>) {
return makeCxxInstance();
}
jni::local_ref<ReadableNativeMap::jhybridobject> StateWrapperImpl::getState() {
folly::dynamic map = state_->getDynamic();
local_ref<ReadableNativeMap::jhybridobject> readableNativeMap =
ReadableNativeMap::newObjectCxxArgs(map);
return readableNativeMap;
}
void StateWrapperImpl::updateStateImpl(NativeMap* map) {
// Get folly::dynamic from map
auto dynamicMap = map->consume();
// Set state
state_->updateState(dynamicMap);
}
void StateWrapperImpl::registerNatives() {
registerHybrid({
makeNativeMethod("initHybrid", StateWrapperImpl::initHybrid),
makeNativeMethod("getState", StateWrapperImpl::getState),
makeNativeMethod("updateStateImpl", StateWrapperImpl::updateStateImpl),
});
}
} // namespace react
} // namespace facebook

View File

@@ -0,0 +1,34 @@
// Copyright 2004-present Facebook. All Rights Reserved.
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
#pragma once
#include <fb/fbjni.h>
#include <react/core/State.h>
#include <react/jni/ReadableNativeMap.h>
namespace facebook {
namespace react {
class Instance;
class StateWrapperImpl : public jni::HybridClass<StateWrapperImpl> {
public:
constexpr static const char* const kJavaDescriptor =
"Lcom/facebook/react/fabric/jsi/StateWrapperImpl;";
static void registerNatives();
jni::local_ref<ReadableNativeMap::jhybridobject> getState();
void updateStateImpl(NativeMap *map);
const State* state_;
private:
jni::alias_ref<StateWrapperImpl::jhybriddata> jhybridobject_;
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jclass>);
};
} // namespace react
} // namespace facebook

View File

@@ -17,6 +17,7 @@ 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.fabric.FabricUIManager;
@@ -26,11 +27,13 @@ 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.StateWrapper;
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.yoga.YogaMeasureMode;
import android.util.Log;
import java.util.concurrent.ConcurrentHashMap;
/**
@@ -270,6 +273,26 @@ public class MountingManager {
}
}
@UiThread
public void updateState(final int reactTag, StateWrapper stateWrapper) {
UiThreadUtil.assertOnUiThread();
ViewState viewState = getViewState(reactTag);
ReadableNativeMap newState = stateWrapper.getState();
if (viewState.mCurrentState != null && viewState.mCurrentState.equals(newState)) {
return;
}
viewState.mCurrentState = newState;
ViewManager viewManager = viewState.mViewManager;
if (viewManager == null) {
throw new IllegalStateException("Unable to find ViewManager for tag: " + reactTag);
}
viewManager.updateState(
viewState.mView,
stateWrapper);
}
@UiThread
public void preallocateView(
ThemedReactContext reactContext,
@@ -326,6 +349,7 @@ public class MountingManager {
@Nullable final ViewManager mViewManager;
public ReactStylesDiffMap mCurrentProps;
public ReadableMap mCurrentLocalData;
public ReadableMap mCurrentState;
public EventEmitterWrapper mEventEmitter;
private ViewState(int reactTag, @Nullable View view, @Nullable ViewManager viewManager) {

View File

@@ -45,7 +45,7 @@ public class BatchMountItem implements MountItem {
@Override
public void execute(MountingManager mountingManager) {
Systrace.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "FabricUIManager::mountViews (" + mSize + " items)");
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "FabricUIManager::mountViews - " + mSize + " items");
for (int mountItemIndex = 0; mountItemIndex < mSize; mountItemIndex++) {
MountItem mountItem = mMountItems[mountItemIndex];

View File

@@ -8,7 +8,6 @@ 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 {

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
* directory of this source tree.
*/
package com.facebook.react.fabric.mounting.mountitems;
import com.facebook.react.fabric.mounting.MountingManager;
import com.facebook.react.uimanager.StateWrapper;
public class UpdateStateMountItem implements MountItem {
private final int mReactTag;
private final StateWrapper mStateWrapper;
public UpdateStateMountItem(int reactTag, StateWrapper stateWrapper) {
mReactTag = reactTag;
mStateWrapper = stateWrapper;
}
@Override
public void execute(MountingManager mountingManager) {
mountingManager.updateState(mReactTag, mStateWrapper);
}
@Override
public String toString() {
return "UpdateStateMountItem [" + mReactTag + "] - stateWrapper: " + mStateWrapper;
}
}

View File

@@ -0,0 +1,29 @@
/**
* 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.uimanager;
import com.facebook.react.bridge.ReadableNativeMap;
import com.facebook.react.bridge.WritableMap;
/**
* This is a wrapper that can be used for passing State objects from Fabric C++
* core to platform-specific components in Java.
* State allows you to break out of uni-directional dataflow by calling updateState,
* which communicates state back to the C++ layer.
*/
public interface StateWrapper {
/**
* Get a ReadableNativeMap object from the C++ layer, which is a K/V map of
* string keys to values.
*/
ReadableNativeMap getState();
/**
* Pass a map of values back to the C++ layer.
*/
void updateState(WritableMap map);
}

View File

@@ -13,6 +13,7 @@ import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.uimanager.StateWrapper;
import com.facebook.react.touch.JSResponderHandler;
import com.facebook.react.touch.ReactInterceptingViewGroup;
import com.facebook.react.uimanager.annotations.ReactProp;
@@ -196,13 +197,17 @@ public abstract class ViewManager<T extends View, C extends ReactShadowNode>
return ViewManagerPropertyUpdater.getNativeProps(getClass(), getShadowNodeClass());
}
/**
*
*/
public @Nullable Object updateLocalData(@Nonnull T view, ReactStylesDiffMap props, ReactStylesDiffMap localData) {
public @Nullable Object updateLocalData( @Nonnull T view, ReactStylesDiffMap props, ReactStylesDiffMap localData) {
return null;
}
/**
* Subclasses can implement this method to receive state updates shared between all instances
* of this component type.
*/
public void updateState(@Nonnull T view, StateWrapper stateWrapper) {
}
public long measure(
ReactContext context,
ReadableMap localData,