From 5be0dff433f65cbc1d09d9bafaeda0406a986a72 Mon Sep 17 00:00:00 2001 From: "Andrew Chen (Eng)" Date: Tue, 2 Oct 2018 13:54:31 -0700 Subject: [PATCH] Avoid view manager class loads (take 2) Summary: Second attempt at landing D9930713. Notes about the previous issue is mentioned as an inline comment below. =========== We are currently iterating through each view manager to get its class name to pass to JS. JS uses this list to define lazy property accesses for each view manager to grab the constants synchronously. This results in each view manager's class loading immediately -- causing a small perf hit. Let's avoid this view managers list entirely. JS is able to access each view manager directly by calling getConstantsForViewManager(name) Reviewed By: axe-fb Differential Revision: D10118711 fbshipit-source-id: 78de8f34db364a64f5ce6af70e3d8691353b0d4d --- Libraries/ReactNative/UIManager.js | 37 +++++++++- Libraries/ReactNative/UIManagerProperties.js | 67 +++++++++++++++++++ .../getNativeComponentAttributes.js | 2 +- .../java/com/facebook/react/uimanager/BUCK | 2 +- .../UIManagerModuleConstantsHelper.java | 7 +- 5 files changed, 110 insertions(+), 5 deletions(-) create mode 100644 Libraries/ReactNative/UIManagerProperties.js diff --git a/Libraries/ReactNative/UIManager.js b/Libraries/ReactNative/UIManager.js index c93563011..1887bedf8 100644 --- a/Libraries/ReactNative/UIManager.js +++ b/Libraries/ReactNative/UIManager.js @@ -11,11 +11,13 @@ const NativeModules = require('NativeModules'); const Platform = require('Platform'); +const UIManagerProperties = require('UIManagerProperties'); const defineLazyObjectProperty = require('defineLazyObjectProperty'); const invariant = require('fbjs/lib/invariant'); const {UIManager} = NativeModules; +const viewManagerConfigs = {}; invariant( UIManager, @@ -36,7 +38,20 @@ UIManager.takeSnapshot = function() { ); }; UIManager.getViewManagerConfig = function(viewManagerName: string) { - return UIManager[viewManagerName]; + if ( + viewManagerConfigs[viewManagerName] === undefined && + UIManager.getConstantsForViewManager + ) { + try { + viewManagerConfigs[ + viewManagerName + ] = UIManager.getConstantsForViewManager(viewManagerName); + } catch (e) { + viewManagerConfigs[viewManagerName] = null; + } + } + + return viewManagerConfigs[viewManagerName]; }; /** @@ -48,6 +63,7 @@ if (Platform.OS === 'ios') { Object.keys(UIManager).forEach(viewName => { const viewConfig = UIManager[viewName]; if (viewConfig.Manager) { + viewManagerConfigs[viewName] = viewConfig; defineLazyObjectProperty(viewConfig, 'Constants', { get: () => { const viewManager = NativeModules[viewConfig.Manager]; @@ -107,4 +123,23 @@ if (Platform.OS === 'ios') { if (global.__makePartial) global.__makePartial(UIManager); } +if (__DEV__) { + Object.keys(UIManager).forEach(viewManagerName => { + if (!UIManagerProperties.includes(viewManagerName)) { + if (!viewManagerConfigs[viewManagerName]) { + viewManagerConfigs[viewManagerName] = UIManager[viewManagerName]; + } + defineLazyObjectProperty(UIManager, viewManagerName, { + get: () => { + console.warn( + `Accessing view manager configs directly off UIManager via UIManager['${viewManagerName}'] ` + + `is no longer supported. Use UIManager.getViewManager('${viewManagerName}') instead.`, + ); + return UIManager.getViewManagerConfig(viewManagerName); + }, + }); + } + }); +} + module.exports = UIManager; diff --git a/Libraries/ReactNative/UIManagerProperties.js b/Libraries/ReactNative/UIManagerProperties.js new file mode 100644 index 000000000..a579b6047 --- /dev/null +++ b/Libraries/ReactNative/UIManagerProperties.js @@ -0,0 +1,67 @@ +/** + * 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. + * + * @flow strict-local + * @format + */ +'use strict'; + +/** + * The list of non-ViewManager related UIManager properties. + * + * In an effort to improve startup performance by lazily loading view managers, + * the interface to access view managers will change from + * UIManager['viewManagerName'] to UIManager.getViewManager('viewManagerName'). + * By using a function call instead of a property access, the UIManager will + * be able to initialize and load the required view manager from native + * synchronously. All of React Native's core components have been updated to + * use getViewManager(). For the next few releases, any usage of + * UIManager['viewManagerName'] will result in a warning. Because React Native + * does not support Proxy objects, a view manager access is implied if any of + * UIManager's properties that are not one of the properties below is being + * accessed. Once UIManager property accesses for view managers has been fully + * deprecated, this file will also be removed. + */ +module.exports = [ + 'clearJSResponder', + 'configureNextLayoutAnimation', + 'createView', + 'dismissPopupMenu', + 'dispatchViewManagerCommand', + 'findSubviewIn', + 'getConstantsForViewManager', + 'getDefaultEventTypes', + 'manageChildren', + 'measure', + 'measureInWindow', + 'measureLayout', + 'measureLayoutRelativeToParent', + 'playTouchSound', + 'removeRootView', + 'removeSubviewsFromContainerWithID', + 'replaceExistingNonRootView', + 'sendAccessibilityEvent', + 'setChildren', + 'setJSResponder', + 'setLayoutAnimationEnabledExperimental', + 'showPopupMenu', + 'updateView', + 'viewIsDescendantOf', + 'PopupMenu', + 'LazyViewManagersEnabled', + 'ViewManagerNames', + 'StyleConstants', + 'AccessibilityEventTypes', + 'UIView', + '__takeSnapshot', + 'takeSnapshot', + 'getViewManagerConfig', + 'measureViewsInRect', + 'blur', + 'focus', + 'genericBubblingEventTypes', + 'genericDirectEventTypes', +]; diff --git a/Libraries/ReactNative/getNativeComponentAttributes.js b/Libraries/ReactNative/getNativeComponentAttributes.js index 64771945f..6a8c54914 100644 --- a/Libraries/ReactNative/getNativeComponentAttributes.js +++ b/Libraries/ReactNative/getNativeComponentAttributes.js @@ -96,7 +96,7 @@ function attachDefaultEventTypes(viewConfig: any) { // This is supported on UIManager platforms (ex: Android), // as lazy view managers are not implemented for all platforms. // See [UIManager] for details on constants and implementations. - if (UIManager.ViewManagerNames) { + if (UIManager.ViewManagerNames || UIManager.LazyViewManagersEnabled) { // Lazy view managers enabled. viewConfig = merge(viewConfig, UIManager.getDefaultEventTypes()); } else { diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BUCK b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BUCK index 262d60c93..9bb553e2e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BUCK @@ -30,7 +30,7 @@ rn_android_library( react_native_target("java/com/facebook/react/animation:animation"), 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/common:common"), + react_native_target("java/com/facebook/react/config:config"), react_native_target("java/com/facebook/react/module/annotations:annotations"), react_native_target("java/com/facebook/react/modules/core:core"), react_native_target("java/com/facebook/react/modules/i18nmanager:i18nmanager"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstantsHelper.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstantsHelper.java index a6aed9343..8e993c193 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstantsHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstantsHelper.java @@ -10,7 +10,7 @@ package com.facebook.react.uimanager; import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE; import com.facebook.react.common.MapBuilder; -import com.facebook.systrace.Systrace; +import com.facebook.react.config.ReactFeatureFlags; import com.facebook.systrace.SystraceMessage; import java.util.List; import java.util.Map; @@ -35,7 +35,10 @@ import javax.annotation.Nullable; /* package */ static Map createConstants( UIManagerModule.ViewManagerResolver resolver) { Map constants = UIManagerModuleConstants.getConstants(); - constants.put("ViewManagerNames", resolver.getViewManagerNames()); + if (!ReactFeatureFlags.lazilyLoadViewManagers) { + constants.put("ViewManagerNames", resolver.getViewManagerNames()); + } + constants.put("LazyViewManagersEnabled", true); return constants; }