mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-02-09 22:50:21 +08:00
Sync diff : Enable initializing react context off UI thread
Reviewed By: @astreet Differential Revision: D2480130
This commit is contained in:
committed by
facebook-github-bot-2
parent
8f13560fd5
commit
dcae4bada0
@@ -16,6 +16,7 @@ import java.util.List;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
@@ -72,6 +73,8 @@ public class ReactInstanceManager {
|
||||
/* should only be accessed from main thread (UI thread) */
|
||||
private final List<ReactRootView> mAttachedRootViews = new ArrayList<>();
|
||||
private LifecycleState mLifecycleState;
|
||||
private boolean mIsContextInitAsyncTaskRunning;
|
||||
private @Nullable ReactContextInitParams mPendingReactContextInitParams;
|
||||
|
||||
/* accessed from any thread */
|
||||
private final @Nullable String mBundleAssetName; /* name of JS bundle file in assets folder */
|
||||
@@ -105,11 +108,70 @@ public class ReactInstanceManager {
|
||||
|
||||
private final DefaultHardwareBackBtnHandler mBackBtnHandler =
|
||||
new DefaultHardwareBackBtnHandler() {
|
||||
@Override
|
||||
public void invokeDefaultOnBackPressed() {
|
||||
ReactInstanceManager.this.invokeDefaultOnBackPressed();
|
||||
@Override
|
||||
public void invokeDefaultOnBackPressed() {
|
||||
ReactInstanceManager.this.invokeDefaultOnBackPressed();
|
||||
}
|
||||
};
|
||||
|
||||
private class ReactContextInitParams {
|
||||
private final JavaScriptExecutor mJsExecutor;
|
||||
private final JSBundleLoader mJsBundleLoader;
|
||||
|
||||
public ReactContextInitParams(
|
||||
JavaScriptExecutor jsExecutor,
|
||||
JSBundleLoader jsBundleLoader) {
|
||||
mJsExecutor = Assertions.assertNotNull(jsExecutor);
|
||||
mJsBundleLoader = Assertions.assertNotNull(jsBundleLoader);
|
||||
}
|
||||
};
|
||||
|
||||
public JavaScriptExecutor getJsExecutor() {
|
||||
return mJsExecutor;
|
||||
}
|
||||
|
||||
public JSBundleLoader getJsBundleLoader() {
|
||||
return mJsBundleLoader;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Task class responsible for (re)creating react context in the background. These tasks can only
|
||||
* be executing one at time, see {@link #recreateReactContextInBackground()}.
|
||||
*/
|
||||
private final class ReactContextInitAsyncTask extends
|
||||
AsyncTask<ReactContextInitParams, Void, ReactApplicationContext> {
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
if (mCurrentReactContext != null) {
|
||||
tearDownReactContext(mCurrentReactContext);
|
||||
mCurrentReactContext = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReactApplicationContext doInBackground(ReactContextInitParams... params) {
|
||||
Assertions.assertCondition(params != null && params.length > 0 && params[0] != null);
|
||||
return createReactContext(params[0].getJsExecutor(), params[0].getJsBundleLoader());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(ReactApplicationContext reactContext) {
|
||||
try {
|
||||
setupReactContext(reactContext);
|
||||
} finally {
|
||||
mIsContextInitAsyncTaskRunning = false;
|
||||
}
|
||||
|
||||
// Handle enqueued request to re-initialize react context.
|
||||
if (mPendingReactContextInitParams != null) {
|
||||
recreateReactContextInBackground(
|
||||
mPendingReactContextInitParams.getJsExecutor(),
|
||||
mPendingReactContextInitParams.getJsBundleLoader());
|
||||
mPendingReactContextInitParams = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ReactInstanceManager(
|
||||
Context applicationContext,
|
||||
@@ -161,10 +223,35 @@ public class ReactInstanceManager {
|
||||
SoLoader.init(applicationContext, /* native exopackage */ false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger react context initialization asynchronously in a background async task. This enables
|
||||
* applications to pre-load the application JS, and execute global code before
|
||||
* {@link ReactRootView} is available and measured.
|
||||
*/
|
||||
public void createReactContextInBackground() {
|
||||
if (mUseDeveloperSupport) {
|
||||
if (mDevSupportManager.hasUpToDateJSBundleInCache()) {
|
||||
// If there is a up-to-date bundle downloaded from server, always use that
|
||||
onJSBundleLoadedFromServer();
|
||||
return;
|
||||
} else if (mBundleAssetName == null ||
|
||||
!mDevSupportManager.hasBundleInAssets(mBundleAssetName)) {
|
||||
// Bundle not available in assets, fetch from the server
|
||||
mDevSupportManager.handleReloadJS();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Use JS file from assets
|
||||
recreateReactContextInBackground(
|
||||
new JSCJavaScriptExecutor(),
|
||||
JSBundleLoader.createAssetLoader(
|
||||
mApplicationContext.getAssets(),
|
||||
mBundleAssetName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public void onBackPressed() {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
@@ -255,16 +342,24 @@ public class ReactInstanceManager {
|
||||
|
||||
/**
|
||||
* Attach given {@param rootView} to a catalyst instance manager and start JS application using
|
||||
* JS module provided by {@link ReactRootView#getJSModuleName}. This view will then be tracked
|
||||
* by this manager and in case of catalyst instance restart it will be re-attached.
|
||||
* 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
|
||||
* be re-attached.
|
||||
*/
|
||||
/* package */ void attachMeasuredRootView(ReactRootView rootView) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
mAttachedRootViews.add(rootView);
|
||||
if (mCurrentReactContext == null) {
|
||||
initializeReactContext();
|
||||
} else {
|
||||
attachMeasuredRootViewToInstance(rootView, mCurrentReactContext.getCatalystInstance());
|
||||
|
||||
// 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.
|
||||
if (!mIsContextInitAsyncTaskRunning) {
|
||||
if (mCurrentReactContext == null) {
|
||||
createReactContextInBackground();
|
||||
} else {
|
||||
attachMeasuredRootViewToInstance(rootView, mCurrentReactContext.getCatalystInstance());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,53 +395,51 @@ public class ReactInstanceManager {
|
||||
}
|
||||
|
||||
private void onReloadWithJSDebugger(ProxyJavaScriptExecutor proxyExecutor) {
|
||||
recreateReactContext(
|
||||
recreateReactContextInBackground(
|
||||
proxyExecutor,
|
||||
JSBundleLoader.createRemoteDebuggerBundleLoader(
|
||||
mDevSupportManager.getJSBundleURLForRemoteDebugging()));
|
||||
}
|
||||
|
||||
private void onJSBundleLoadedFromServer() {
|
||||
recreateReactContext(
|
||||
recreateReactContextInBackground(
|
||||
new JSCJavaScriptExecutor(),
|
||||
JSBundleLoader.createCachedBundleFromNetworkLoader(
|
||||
mDevSupportManager.getSourceUrl(),
|
||||
mDevSupportManager.getDownloadedJSBundleFile()));
|
||||
}
|
||||
|
||||
private void initializeReactContext() {
|
||||
if (mUseDeveloperSupport) {
|
||||
if (mDevSupportManager.hasUpToDateJSBundleInCache()) {
|
||||
// If there is a up-to-date bundle downloaded from server, always use that
|
||||
onJSBundleLoadedFromServer();
|
||||
return;
|
||||
} else if (mBundleAssetName == null ||
|
||||
!mDevSupportManager.hasBundleInAssets(mBundleAssetName)) {
|
||||
// Bundle not available in assets, fetch from the server
|
||||
mDevSupportManager.handleReloadJS();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Use JS file from assets
|
||||
recreateReactContext(
|
||||
new JSCJavaScriptExecutor(),
|
||||
JSBundleLoader.createAssetLoader(
|
||||
mApplicationContext.getAssets(),
|
||||
mBundleAssetName));
|
||||
}
|
||||
|
||||
private void recreateReactContext(
|
||||
private void recreateReactContextInBackground(
|
||||
JavaScriptExecutor jsExecutor,
|
||||
JSBundleLoader jsBundleLoader) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
if (mCurrentReactContext != null) {
|
||||
tearDownReactContext(mCurrentReactContext);
|
||||
|
||||
ReactContextInitParams initParams = new ReactContextInitParams(jsExecutor, jsBundleLoader);
|
||||
if (!mIsContextInitAsyncTaskRunning) {
|
||||
// No background task to create react context is currently running, create and execute one.
|
||||
ReactContextInitAsyncTask initTask = new ReactContextInitAsyncTask();
|
||||
initTask.execute(initParams);
|
||||
mIsContextInitAsyncTaskRunning = true;
|
||||
} else {
|
||||
// Background task is currently running, queue up most recent init params to recreate context
|
||||
// once task completes.
|
||||
mPendingReactContextInitParams = initParams;
|
||||
}
|
||||
mCurrentReactContext = createReactContext(jsExecutor, jsBundleLoader);
|
||||
}
|
||||
|
||||
private void setupReactContext(ReactApplicationContext reactContext) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
Assertions.assertCondition(mCurrentReactContext == null);
|
||||
mCurrentReactContext = Assertions.assertNotNull(reactContext);
|
||||
CatalystInstance catalystInstance =
|
||||
Assertions.assertNotNull(reactContext.getCatalystInstance());
|
||||
|
||||
catalystInstance.initialize();
|
||||
mDevSupportManager.onNewReactContextCreated(reactContext);
|
||||
moveReactContextToCurrentLifecycleState(reactContext);
|
||||
|
||||
for (ReactRootView rootView : mAttachedRootViews) {
|
||||
attachMeasuredRootViewToInstance(
|
||||
rootView,
|
||||
mCurrentReactContext.getCatalystInstance());
|
||||
attachMeasuredRootViewToInstance(rootView, catalystInstance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,10 +523,6 @@ public class ReactInstanceManager {
|
||||
}
|
||||
|
||||
reactContext.initializeWithInstance(catalystInstance);
|
||||
catalystInstance.initialize();
|
||||
mDevSupportManager.onNewReactContextCreated(reactContext);
|
||||
|
||||
moveReactContextToCurrentLifecycleState(reactContext);
|
||||
|
||||
return reactContext;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user