Detach dependency on RRV for Instance Manager

Summary: Remove dependency on ReactRootView in ReactInstanceManager by creating a rough interface (ReactRoot) so that either a Surface or ReactRootView can be attached to ReactInstanceManager.

Reviewed By: ejanzer, mdvacca

Differential Revision: D14158890

fbshipit-source-id: b7ab4654b1e0ef8343230a3c15023653a7f23a4b
This commit is contained in:
Luna Wei
2019-04-10 13:07:33 -07:00
committed by Facebook Github Bot
parent 417adf526f
commit 135ba492fb
6 changed files with 180 additions and 56 deletions

View File

@@ -35,6 +35,7 @@ rn_android_library(
react_native_target("java/com/facebook/react/modules/deviceinfo:deviceinfo"),
react_native_target("java/com/facebook/react/modules/systeminfo:systeminfo"),
react_native_target("java/com/facebook/react/modules/toast:toast"),
react_native_target("java/com/facebook/react/surface:surface"),
react_native_target("java/com/facebook/react/uimanager:uimanager"),
react_native_target("java/com/facebook/react/module/annotations:annotations"),
react_native_target("java/com/facebook/react/views/imagehelper:imagehelper"),

View File

@@ -84,7 +84,9 @@ import com.facebook.react.modules.core.ReactChoreographer;
import com.facebook.react.modules.debug.interfaces.DeveloperSettings;
import com.facebook.react.modules.fabric.ReactFabric;
import com.facebook.react.packagerconnection.RequestHandler;
import com.facebook.react.surface.ReactStage;
import com.facebook.react.uimanager.DisplayMetricsHolder;
import com.facebook.react.uimanager.ReactRoot;
import com.facebook.react.uimanager.UIImplementationProvider;
import com.facebook.react.uimanager.UIManagerHelper;
import com.facebook.react.uimanager.ViewManager;
@@ -99,7 +101,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.Nullable;
/**
@@ -135,8 +136,8 @@ public class ReactInstanceManager {
void onReactContextInitialized(ReactContext context);
}
private final Set<ReactRootView> mAttachedRootViews = Collections.synchronizedSet(
new HashSet<ReactRootView>());
private final Set<ReactRoot> mAttachedReactRoots = Collections.synchronizedSet(
new HashSet<ReactRoot>());
private volatile LifecycleState mLifecycleState;
@@ -710,45 +711,49 @@ public class ReactInstanceManager {
mDevSupportManager.showDevOptionsDialog();
}
private void clearReactRoot(ReactRoot reactRoot) {
reactRoot.getRootViewGroup().removeAllViews();
reactRoot.getRootViewGroup().setId(View.NO_ID);
}
/**
* Attach given {@param rootView} to a catalyst instance manager and start JS application using
* Attach given {@param reactRoot} to a catalyst instance manager and start JS application using
* JS module provided by {@link ReactRootView#getJSModuleName}. If the react context is currently
* being (re)-created, or if react context has not been created yet, the JS application associated
* with the provided root view will be started asynchronously, i.e this method won't block.
* This view will then be tracked by this manager and in case of catalyst instance restart it will
* with the provided reactRoot reactRoot will be started asynchronously, i.e this method won't block.
* This reactRoot will then be tracked by this manager and in case of catalyst instance restart it will
* be re-attached.
*/
@ThreadConfined(UI)
public void attachRootView(ReactRootView rootView) {
public void attachRootView(ReactRoot reactRoot) {
UiThreadUtil.assertOnUiThread();
mAttachedRootViews.add(rootView);
mAttachedReactRoots.add(reactRoot);
// Reset view content as it's going to be populated by the application content from JS.
rootView.removeAllViews();
rootView.setId(View.NO_ID);
// Reset reactRoot content as it's going to be populated by the application content from JS.
clearReactRoot(reactRoot);
// If react context is being created in the background, JS application will be started
// automatically when creation completes, as root view is part of the attached root view list.
// automatically when creation completes, as reactRoot reactRoot is part of the attached reactRoot reactRoot list.
ReactContext currentContext = getCurrentReactContext();
if (mCreateReactContextThread == null && currentContext != null) {
attachRootViewToInstance(rootView);
attachRootViewToInstance(reactRoot);
}
}
/**
* Detach given {@param rootView} from current catalyst instance. It's safe to call this method
* multiple times on the same {@param rootView} - in that case view will be detached with the
* Detach given {@param reactRoot} from current catalyst instance. It's safe to call this method
* multiple times on the same {@param reactRoot} - in that case view will be detached with the
* first call.
*/
@ThreadConfined(UI)
public void detachRootView(ReactRootView rootView) {
public void detachRootView(ReactRoot reactRoot) {
UiThreadUtil.assertOnUiThread();
synchronized (mAttachedRootViews) {
if (mAttachedRootViews.contains(rootView)) {
synchronized (mAttachedReactRoots) {
if (mAttachedReactRoots.contains(reactRoot)) {
ReactContext currentContext = getCurrentReactContext();
mAttachedRootViews.remove(rootView);
mAttachedReactRoots.remove(reactRoot);
if (currentContext != null && currentContext.hasActiveCatalystInstance()) {
detachViewFromInstance(rootView, currentContext.getCatalystInstance());
detachViewFromInstance(reactRoot, currentContext.getCatalystInstance());
}
}
}
@@ -908,7 +913,7 @@ public class ReactInstanceManager {
private void runCreateReactContextOnNewThread(final ReactContextInitParams initParams) {
Log.d(ReactConstants.TAG, "ReactInstanceManager.runCreateReactContextOnNewThread()");
UiThreadUtil.assertOnUiThread();
synchronized (mAttachedRootViews) {
synchronized (mAttachedReactRoots) {
synchronized (mReactContextLock) {
if (mCurrentReactContext != null) {
tearDownReactContext(mCurrentReactContext);
@@ -985,7 +990,7 @@ public class ReactInstanceManager {
ReactMarker.logMarker(PRE_SETUP_REACT_CONTEXT_END);
ReactMarker.logMarker(SETUP_REACT_CONTEXT_START);
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "setupReactContext");
synchronized (mAttachedRootViews) {
synchronized (mAttachedReactRoots) {
synchronized (mReactContextLock) {
mCurrentReactContext = Assertions.assertNotNull(reactContext);
}
@@ -999,8 +1004,8 @@ public class ReactInstanceManager {
moveReactContextToCurrentLifecycleState();
ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_START);
for (ReactRootView rootView : mAttachedRootViews) {
attachRootViewToInstance(rootView);
for (ReactRoot reactRoot : mAttachedReactRoots) {
attachRootViewToInstance(reactRoot);
}
ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_END);
}
@@ -1038,47 +1043,46 @@ public class ReactInstanceManager {
});
}
private void attachRootViewToInstance(final ReactRootView rootView) {
private void attachRootViewToInstance(final ReactRoot reactRoot) {
Log.d(ReactConstants.TAG, "ReactInstanceManager.attachRootViewToInstance()");
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "attachRootViewToInstance");
UIManager uiManagerModule = UIManagerHelper.getUIManager(mCurrentReactContext, rootView.getUIManagerType());
UIManager uiManagerModule = UIManagerHelper.getUIManager(mCurrentReactContext, reactRoot.getUIManagerType());
@Nullable Bundle initialProperties = rootView.getAppProperties();
@Nullable Bundle initialProperties = reactRoot.getAppProperties();
final int rootTag = uiManagerModule.addRootView(
rootView.getView(),
reactRoot.getRootViewGroup(),
initialProperties == null ?
new WritableNativeMap() : Arguments.fromBundle(initialProperties),
rootView.getInitialUITemplate());
rootView.setRootViewTag(rootTag);
rootView.runApplication();
reactRoot.getInitialUITemplate());
reactRoot.setRootViewTag(rootTag);
reactRoot.runApplication();
Systrace.beginAsyncSection(
TRACE_TAG_REACT_JAVA_BRIDGE,
"pre_rootView.onAttachedToReactInstance",
rootTag);
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
Systrace.endAsyncSection(
TRACE_TAG_REACT_JAVA_BRIDGE,
"pre_rootView.onAttachedToReactInstance",
rootTag);
rootView.onAttachedToReactInstance();
}
});
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
Systrace.endAsyncSection(
TRACE_TAG_REACT_JAVA_BRIDGE, "pre_rootView.onAttachedToReactInstance", rootTag);
reactRoot.onStage(ReactStage.ON_ATTACH_TO_INSTANCE);
}
});
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
private void detachViewFromInstance(
ReactRootView rootView,
ReactRoot reactRoot,
CatalystInstance catalystInstance) {
Log.d(ReactConstants.TAG, "ReactInstanceManager.detachViewFromInstance()");
UiThreadUtil.assertOnUiThread();
if (rootView.getUIManagerType() == FABRIC) {
if (reactRoot.getUIManagerType() == FABRIC) {
catalystInstance.getJSModule(ReactFabric.class)
.unmountComponentAtNode(rootView.getRootViewTag());
.unmountComponentAtNode(reactRoot.getRootViewTag());
} else {
catalystInstance.getJSModule(AppRegistry.class)
.unmountApplicationComponentAtRootTag(rootView.getRootViewTag());
.unmountApplicationComponentAtRootTag(reactRoot.getRootViewTag());
}
}
@@ -1090,10 +1094,9 @@ public class ReactInstanceManager {
reactContext.onHostPause();
}
synchronized (mAttachedRootViews) {
for (ReactRootView rootView : mAttachedRootViews) {
rootView.removeAllViews();
rootView.setId(View.NO_ID);
synchronized (mAttachedReactRoots) {
for (ReactRoot reactRoot : mAttachedReactRoots) {
clearReactRoot(reactRoot);
}
}

View File

@@ -41,11 +41,13 @@ import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.modules.appregistry.AppRegistry;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.modules.deviceinfo.DeviceInfoModule;
import com.facebook.react.surface.ReactStage;
import com.facebook.react.uimanager.DisplayMetricsHolder;
import com.facebook.react.uimanager.IllegalViewOperationException;
import com.facebook.react.uimanager.JSTouchDispatcher;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.RootView;
import com.facebook.react.uimanager.ReactRoot;
import com.facebook.react.uimanager.UIManagerHelper;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.common.UIManagerType;
@@ -65,7 +67,7 @@ import javax.annotation.Nullable;
* subsequent touch events related to that gesture (in case when JS code wants to handle that
* gesture).
*/
public class ReactRootView extends FrameLayout implements RootView {
public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
/**
* Listener interface for react root view events
@@ -124,11 +126,6 @@ public class ReactRootView extends FrameLayout implements RootView {
setClipChildren(false);
}
public View getView() {
// TODO add mUseSurface to return surface here
return this;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mUseSurface) {
@@ -347,6 +344,11 @@ public class ReactRootView extends FrameLayout implements RootView {
}
}
@Override
public ViewGroup getRootViewGroup() {
return this;
}
/**
* {@see #startReactApplication(ReactInstanceManager, String, android.os.Bundle)}
*/
@@ -433,6 +435,17 @@ public class ReactRootView extends FrameLayout implements RootView {
mShouldLogContentAppeared = false;
}
@Override
public void onStage(int stage) {
switch(stage) {
case ReactStage.ON_ATTACH_TO_INSTANCE:
onAttachedToReactInstance();
break;
default:
break;
}
}
public void onAttachedToReactInstance() {
// Create the touch dispatcher here instead of having it always available, to make sure
// that all touch events are only passed to JS after React/JS side is ready to consume
@@ -452,10 +465,12 @@ public class ReactRootView extends FrameLayout implements RootView {
return Assertions.assertNotNull(mJSModuleName);
}
@Override
public @Nullable Bundle getAppProperties() {
return mAppProperties;
}
@Override
public @Nullable String getInitialUITemplate() {
return mInitialUITemplate;
}
@@ -472,7 +487,8 @@ public class ReactRootView extends FrameLayout implements RootView {
* Calls into JS to start the React application. Can be called multiple times with the
* same rootTag, which will re-render the application from the root.
*/
/* package */ void runApplication() {
@Override
public void runApplication() {
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "ReactRootView.runApplication");
try {
if (mReactInstanceManager == null || !mIsAttachedToInstance) {
@@ -581,6 +597,7 @@ public class ReactRootView extends FrameLayout implements RootView {
mUIManagerType = isFabric ? FABRIC : DEFAULT;
}
@Override
public @UIManagerType int getUIManagerType() {
return mUIManagerType;
}

View File

@@ -0,0 +1,20 @@
load("@fbsource//tools/build_defs/oss:rn_defs.bzl", "YOGA_TARGET", "react_native_dep", "react_native_target", "rn_android_library")
rn_android_library(
name = "surface",
srcs = glob(["*.java"]),
is_androidx = True,
visibility = [
"PUBLIC",
],
deps = [
YOGA_TARGET,
react_native_dep("libraries/fbcore/src/main/java/com/facebook/common/logging:logging"),
react_native_dep("third-party/android/support-annotations:android-support-annotations"),
react_native_dep("third-party/java/infer-annotations:infer-annotations"),
react_native_dep("third-party/java/jsr-305:jsr-305"),
react_native_target("java/com/facebook/react/bridge:bridge"),
react_native_target("java/com/facebook/react/common:common"),
react_native_target("java/com/facebook/react/module/annotations:annotations"),
],
)

View File

@@ -0,0 +1,33 @@
package com.facebook.react.surface;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
/**
* The stage of the Surface
*/
@Retention(SOURCE)
@IntDef({
ReactStage.SURFACE_DID_INITIALIZE,
ReactStage.BRIDGE_DID_LOAD,
ReactStage.MODULE_DID_LOAD,
ReactStage.SURFACE_DID_RUN,
ReactStage.SURFACE_DID_INITIAL_RENDERING,
ReactStage.SURFACE_DID_INITIAL_LAYOUT,
ReactStage.SURFACE_DID_INITIAL_MOUNTING,
ReactStage.SURFACE_DID_STOP
})
public @interface ReactStage {
int SURFACE_DID_INITIALIZE = 0;
int BRIDGE_DID_LOAD = 1;
int MODULE_DID_LOAD = 2;
int SURFACE_DID_RUN = 3;
int SURFACE_DID_INITIAL_RENDERING = 4;
int SURFACE_DID_INITIAL_LAYOUT = 5;
int SURFACE_DID_INITIAL_MOUNTING = 6;
int SURFACE_DID_STOP = 7;
int ON_ATTACH_TO_INSTANCE = 101;
}

View File

@@ -0,0 +1,50 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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 android.os.Bundle;
import android.view.ViewGroup;
import com.facebook.react.uimanager.common.UIManagerType;
import javax.annotation.Nullable;
/**
* Interface for the root native view of a React native application
*/
public interface ReactRoot {
/**
* Return cached launch properties for app
*/
@Nullable Bundle getAppProperties();
@Nullable String getInitialUITemplate();
/**
* Fabric or Default UI Manager, see {@link UIManagerType}
*/
@UIManagerType int getUIManagerType();
int getRootViewTag();
void setRootViewTag(int rootViewTag);
/**
* Calls into JS to start the React application.
*/
void runApplication();
/**
* Handler for stages {@link com.facebook.react.surface.ReactStage}
*/
void onStage(int stage);
/**
* Return native view for root
*/
ViewGroup getRootViewGroup();
}