Initialize JS executor and context at loadApp time

Summary: Automatically starts loading the RN instance as soon as loadApp is called instead of waiting for the UI to be constructed and attached to the window. This saves ~75ms of cold start time on GN 2011 phones.

Also, updates the contract for createReactContextInBackground such that it is only called once (so that people don't accidentally initialize more than once).

See http://imgur.com/a/d7qVm for systrace visualization.

public

Reviewed By: kmagiera

Differential Revision: D2652279

fb-gh-sync-id: 6e7b1fa5dea091af0d568834e11ed23eb1a7860e
This commit is contained in:
Andy Street 2015-11-18 11:19:31 -08:00 committed by facebook-github-bot-7
parent fea59168cc
commit 530ee3eeac
2 changed files with 48 additions and 1 deletions

View File

@ -91,6 +91,7 @@ public class ReactInstanceManager {
private @Nullable DefaultHardwareBackBtnHandler mDefaultBackButtonImpl; private @Nullable DefaultHardwareBackBtnHandler mDefaultBackButtonImpl;
private String mSourceUrl; private String mSourceUrl;
private @Nullable Activity mCurrentActivity; private @Nullable Activity mCurrentActivity;
private volatile boolean mHasStartedCreatingInitialContext = false;
private final ReactInstanceDevCommandsHandler mDevInterface = private final ReactInstanceDevCommandsHandler mDevInterface =
new ReactInstanceDevCommandsHandler() { new ReactInstanceDevCommandsHandler() {
@ -235,11 +236,41 @@ public class ReactInstanceManager {
/** /**
* Trigger react context initialization asynchronously in a background async task. This enables * Trigger react context initialization asynchronously in a background async task. This enables
* applications to pre-load the application JS, and execute global code before * applications to pre-load the application JS, and execute global code before
* {@link ReactRootView} is available and measured. * {@link ReactRootView} is available and measured. This should only be called the first time the
* application is set up, which is enforced to keep developers from accidentally creating their
* application multiple times without realizing it.
* *
* Called from UI thread. * Called from UI thread.
*/ */
public void createReactContextInBackground() { public void createReactContextInBackground() {
Assertions.assertCondition(
!mHasStartedCreatingInitialContext,
"createReactContextInBackground should only be called when creating the react " +
"application for the first time. When reloading JS, e.g. from a new file, explicitly" +
"use recreateReactContextInBackground");
mHasStartedCreatingInitialContext = true;
recreateReactContextInBackgroundInner();
}
/**
* Recreate the react application and context. This should be called if configuration has
* changed or the developer has requested the app to be reloaded. It should only be called after
* an initial call to createReactContextInBackground.
*
* Called from UI thread.
*/
public void recreateReactContextInBackground() {
Assertions.assertCondition(
mHasStartedCreatingInitialContext,
"recreateReactContextInBackground should only be called after the initial " +
"createReactContextInBackground call.");
recreateReactContextInBackgroundInner();
}
private void recreateReactContextInBackgroundInner() {
UiThreadUtil.assertOnUiThread();
if (mUseDeveloperSupport && mJSMainModuleName != null) { if (mUseDeveloperSupport && mJSMainModuleName != null) {
if (mDevSupportManager.hasUpToDateJSBundleInCache()) { if (mDevSupportManager.hasUpToDateJSBundleInCache()) {
// If there is a up-to-date bundle downloaded from server, always use that // If there is a up-to-date bundle downloaded from server, always use that
@ -277,6 +308,14 @@ public class ReactInstanceManager {
JSBundleLoader.createFileLoader(mApplicationContext, mJSBundleFile)); JSBundleLoader.createFileLoader(mApplicationContext, mJSBundleFile));
} }
/**
* @return whether createReactContextInBackground has been called. Will return false after
* onDestroy until a new initial context has been created.
*/
public boolean hasStartedCreatingInitialContext() {
return mHasStartedCreatingInitialContext;
}
/** /**
* This method will give JS the opportunity to consume the back button event. If JS does not * This method will give JS the opportunity to consume the back button event. If JS does not
* consume the event, mDefaultBackButtonImpl will be invoked at the end of the round trip to JS. * consume the event, mDefaultBackButtonImpl will be invoked at the end of the round trip to JS.
@ -362,6 +401,8 @@ public class ReactInstanceManager {
if (mCurrentReactContext != null) { if (mCurrentReactContext != null) {
mCurrentReactContext.onDestroy(); mCurrentReactContext.onDestroy();
mCurrentReactContext = null;
mHasStartedCreatingInitialContext = false;
} }
} }

View File

@ -295,6 +295,8 @@ public class ReactRootView extends SizeMonitoringFrameLayout implements RootView
ReactInstanceManager reactInstanceManager, ReactInstanceManager reactInstanceManager,
String moduleName, String moduleName,
@Nullable Bundle launchOptions) { @Nullable Bundle launchOptions) {
UiThreadUtil.assertOnUiThread();
// TODO(6788889): Use POJO instead of bundle here, apparently we can't just use WritableMap // TODO(6788889): Use POJO instead of bundle here, apparently we can't just use WritableMap
// here as it may be deallocated in native after passing via JNI bridge, but we want to reuse // here as it may be deallocated in native after passing via JNI bridge, but we want to reuse
// it in the case of re-creating the catalyst instance // it in the case of re-creating the catalyst instance
@ -307,6 +309,10 @@ public class ReactRootView extends SizeMonitoringFrameLayout implements RootView
mJSModuleName = moduleName; mJSModuleName = moduleName;
mLaunchOptions = launchOptions; mLaunchOptions = launchOptions;
if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
mReactInstanceManager.createReactContextInBackground();
}
// We need to wait for the initial onMeasure, if this view has not yet been measured, we set // We need to wait for the initial onMeasure, if this view has not yet been measured, we set
// mAttachScheduled flag, which will make this view startReactApplication itself to instance // mAttachScheduled flag, which will make this view startReactApplication itself to instance
// manager once onMeasure is called. // manager once onMeasure is called.