mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-24 04:16:00 +08:00
Delete old bridge
Reviewed By: astreet Differential Revision: D3510660 fbshipit-source-id: 031b9dcf19dd4e6677a6c9417917930bcbbe3219
This commit is contained in:
committed by
Facebook Github Bot 3
parent
5617d41327
commit
1a690d5674
@@ -202,7 +202,6 @@ public abstract class ReactInstanceManager {
|
||||
protected @Nullable Activity mCurrentActivity;
|
||||
protected @Nullable DefaultHardwareBackBtnHandler mDefaultHardwareBackBtnHandler;
|
||||
protected @Nullable RedBoxHandler mRedBoxHandler;
|
||||
protected boolean mUseOldBridge;
|
||||
|
||||
protected Builder() {
|
||||
}
|
||||
@@ -330,11 +329,6 @@ public abstract class ReactInstanceManager {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setUseOldBridge(boolean enable) {
|
||||
mUseOldBridge = enable;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new {@link ReactInstanceManagerImpl}.
|
||||
* Before calling {@code build}, the following must be called:
|
||||
@@ -350,10 +344,6 @@ public abstract class ReactInstanceManager {
|
||||
mApplication,
|
||||
"Application property has not been set with this builder");
|
||||
|
||||
Assertions.assertCondition(
|
||||
mJSBundleLoader == null || !mUseOldBridge,
|
||||
"JSBundleLoader can't be used with the old bridge");
|
||||
|
||||
Assertions.assertCondition(
|
||||
mUseDeveloperSupport || mJSBundleFile != null || mJSBundleLoader != null,
|
||||
"JS Bundle File has to be provided when dev support is disabled");
|
||||
@@ -367,38 +357,21 @@ public abstract class ReactInstanceManager {
|
||||
mUIImplementationProvider = new UIImplementationProvider();
|
||||
}
|
||||
|
||||
if (mUseOldBridge) {
|
||||
return new ReactInstanceManagerImpl(
|
||||
mApplication,
|
||||
mCurrentActivity,
|
||||
mDefaultHardwareBackBtnHandler,
|
||||
mJSBundleFile,
|
||||
mJSMainModuleName,
|
||||
mPackages,
|
||||
mUseDeveloperSupport,
|
||||
mBridgeIdleDebugListener,
|
||||
Assertions.assertNotNull(mInitialLifecycleState, "Initial lifecycle state was not set"),
|
||||
mUIImplementationProvider,
|
||||
mNativeModuleCallExceptionHandler,
|
||||
mJSCConfig,
|
||||
mRedBoxHandler);
|
||||
} else {
|
||||
return new XReactInstanceManagerImpl(
|
||||
mApplication,
|
||||
mCurrentActivity,
|
||||
mDefaultHardwareBackBtnHandler,
|
||||
(mJSBundleLoader == null && mJSBundleFile != null) ?
|
||||
JSBundleLoader.createFileLoader(mApplication, mJSBundleFile) : mJSBundleLoader,
|
||||
mJSMainModuleName,
|
||||
mPackages,
|
||||
mUseDeveloperSupport,
|
||||
mBridgeIdleDebugListener,
|
||||
Assertions.assertNotNull(mInitialLifecycleState, "Initial lifecycle state was not set"),
|
||||
mUIImplementationProvider,
|
||||
mNativeModuleCallExceptionHandler,
|
||||
mJSCConfig,
|
||||
mRedBoxHandler);
|
||||
}
|
||||
return new XReactInstanceManagerImpl(
|
||||
mApplication,
|
||||
mCurrentActivity,
|
||||
mDefaultHardwareBackBtnHandler,
|
||||
(mJSBundleLoader == null && mJSBundleFile != null) ?
|
||||
JSBundleLoader.createFileLoader(mApplication, mJSBundleFile) : mJSBundleLoader,
|
||||
mJSMainModuleName,
|
||||
mPackages,
|
||||
mUseDeveloperSupport,
|
||||
mBridgeIdleDebugListener,
|
||||
Assertions.assertNotNull(mInitialLifecycleState, "Initial lifecycle state was not set"),
|
||||
mUIImplementationProvider,
|
||||
mNativeModuleCallExceptionHandler,
|
||||
mJSCConfig,
|
||||
mRedBoxHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,942 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.CatalystInstance;
|
||||
import com.facebook.react.bridge.CatalystInstanceImpl;
|
||||
import com.facebook.react.bridge.JSBundleLoader;
|
||||
import com.facebook.react.bridge.JSCJavaScriptExecutor;
|
||||
import com.facebook.react.bridge.JavaJSExecutor;
|
||||
import com.facebook.react.bridge.JavaScriptExecutor;
|
||||
import com.facebook.react.bridge.JavaScriptModule;
|
||||
import com.facebook.react.bridge.JavaScriptModuleRegistry;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.bridge.NativeModuleCallExceptionHandler;
|
||||
import com.facebook.react.bridge.NativeModuleRegistry;
|
||||
import com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener;
|
||||
import com.facebook.react.bridge.ProxyJavaScriptExecutor;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReactMarker;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.bridge.WritableNativeMap;
|
||||
import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec;
|
||||
import com.facebook.react.common.ApplicationHolder;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.common.annotations.VisibleForTesting;
|
||||
import com.facebook.react.devsupport.DevServerHelper;
|
||||
import com.facebook.react.devsupport.DevSupportManager;
|
||||
import com.facebook.react.devsupport.DevSupportManagerFactory;
|
||||
import com.facebook.react.devsupport.ReactInstanceDevCommandsHandler;
|
||||
import com.facebook.react.devsupport.RedBoxHandler;
|
||||
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper;
|
||||
import com.facebook.react.uimanager.AppRegistry;
|
||||
import com.facebook.react.uimanager.DisplayMetricsHolder;
|
||||
import com.facebook.react.uimanager.UIImplementationProvider;
|
||||
import com.facebook.react.uimanager.UIManagerModule;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
import com.facebook.systrace.Systrace;
|
||||
|
||||
import static com.facebook.react.bridge.ReactMarkerConstants.BUILD_NATIVE_MODULE_REGISTRY_END;
|
||||
import static com.facebook.react.bridge.ReactMarkerConstants.BUILD_NATIVE_MODULE_REGISTRY_START;
|
||||
import static com.facebook.react.bridge.ReactMarkerConstants.CREATE_CATALYST_INSTANCE_END;
|
||||
import static com.facebook.react.bridge.ReactMarkerConstants.CREATE_CATALYST_INSTANCE_START;
|
||||
import static com.facebook.react.bridge.ReactMarkerConstants.CREATE_REACT_CONTEXT_START;
|
||||
import static com.facebook.react.bridge.ReactMarkerConstants.PROCESS_PACKAGES_END;
|
||||
import static com.facebook.react.bridge.ReactMarkerConstants.PROCESS_PACKAGES_START;
|
||||
import static com.facebook.react.bridge.ReactMarkerConstants.RUN_JS_BUNDLE_START;
|
||||
|
||||
/**
|
||||
* This class manages instances of {@link CatalystInstance}. It exposes a way to configure
|
||||
* catalyst instances using {@link ReactPackage} and keeps track of the lifecycle of that
|
||||
* instance. It also sets up connection between the instance and developers support functionality
|
||||
* of the framework.
|
||||
*
|
||||
* An instance of this manager is required to start JS application in {@link ReactRootView} (see
|
||||
* {@link ReactRootView#startReactApplication} for more info).
|
||||
*
|
||||
* The lifecycle of the instance of {@link ReactInstanceManagerImpl} should be bound to the activity
|
||||
* that owns the {@link ReactRootView} that is used to render react application using this
|
||||
* instance manager (see {@link ReactRootView#startReactApplication}). It's required to pass
|
||||
* owning activity's lifecycle events to the instance manager (see {@link #onHostPause},
|
||||
* {@link #onHostDestroy} and {@link #onHostResume}).
|
||||
*
|
||||
* To instantiate an instance of this class use {@link #builder}.
|
||||
*/
|
||||
/* package */ class ReactInstanceManagerImpl extends ReactInstanceManager {
|
||||
|
||||
/* should only be accessed from main thread (UI thread) */
|
||||
private final List<ReactRootView> mAttachedRootViews = new ArrayList<>();
|
||||
private LifecycleState mLifecycleState;
|
||||
private @Nullable ReactContextInitParams mPendingReactContextInitParams;
|
||||
private @Nullable ReactContextInitAsyncTask mReactContextInitAsyncTask;
|
||||
|
||||
/* accessed from any thread */
|
||||
private @Nullable String mJSBundleFile; /* path to JS bundle on file system */
|
||||
private final @Nullable String mJSMainModuleName; /* path to JS bundle root on packager server */
|
||||
private final List<ReactPackage> mPackages;
|
||||
private final DevSupportManager mDevSupportManager;
|
||||
private final boolean mUseDeveloperSupport;
|
||||
private final @Nullable NotThreadSafeBridgeIdleDebugListener mBridgeIdleDebugListener;
|
||||
private @Nullable volatile ReactContext mCurrentReactContext;
|
||||
private final Context mApplicationContext;
|
||||
private @Nullable DefaultHardwareBackBtnHandler mDefaultBackButtonImpl;
|
||||
private String mSourceUrl;
|
||||
private @Nullable Activity mCurrentActivity;
|
||||
private final Collection<ReactInstanceEventListener> mReactInstanceEventListeners =
|
||||
Collections.synchronizedSet(new HashSet<ReactInstanceEventListener>());
|
||||
private volatile boolean mHasStartedCreatingInitialContext = false;
|
||||
private final UIImplementationProvider mUIImplementationProvider;
|
||||
private final MemoryPressureRouter mMemoryPressureRouter;
|
||||
private final @Nullable NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler;
|
||||
private final @Nullable JSCConfig mJSCConfig;
|
||||
private @Nullable RedBoxHandler mRedBoxHandler;
|
||||
|
||||
private final ReactInstanceDevCommandsHandler mDevInterface =
|
||||
new ReactInstanceDevCommandsHandler() {
|
||||
|
||||
@Override
|
||||
public void onReloadWithJSDebugger(JavaJSExecutor.Factory jsExecutorFactory) {
|
||||
ReactInstanceManagerImpl.this.onReloadWithJSDebugger(jsExecutorFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onJSBundleLoadedFromServer() {
|
||||
ReactInstanceManagerImpl.this.onJSBundleLoadedFromServer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toggleElementInspector() {
|
||||
ReactInstanceManagerImpl.this.toggleElementInspector();
|
||||
}
|
||||
};
|
||||
|
||||
private final DefaultHardwareBackBtnHandler mBackBtnHandler =
|
||||
new DefaultHardwareBackBtnHandler() {
|
||||
@Override
|
||||
public void invokeDefaultOnBackPressed() {
|
||||
ReactInstanceManagerImpl.this.invokeDefaultOnBackPressed();
|
||||
}
|
||||
};
|
||||
|
||||
private class ReactContextInitParams {
|
||||
private final JavaScriptExecutor.Factory mJsExecutorFactory;
|
||||
private final JSBundleLoader mJsBundleLoader;
|
||||
|
||||
public ReactContextInitParams(
|
||||
JavaScriptExecutor.Factory jsExecutorFactory,
|
||||
JSBundleLoader jsBundleLoader) {
|
||||
mJsExecutorFactory = Assertions.assertNotNull(jsExecutorFactory);
|
||||
mJsBundleLoader = Assertions.assertNotNull(jsBundleLoader);
|
||||
}
|
||||
|
||||
public JavaScriptExecutor.Factory getJsExecutorFactory() {
|
||||
return mJsExecutorFactory;
|
||||
}
|
||||
|
||||
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, Result<ReactApplicationContext>> {
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
if (mCurrentReactContext != null) {
|
||||
tearDownReactContext(mCurrentReactContext);
|
||||
mCurrentReactContext = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Result<ReactApplicationContext> doInBackground(ReactContextInitParams... params) {
|
||||
Assertions.assertCondition(params != null && params.length > 0 && params[0] != null);
|
||||
try {
|
||||
JavaScriptExecutor jsExecutor =
|
||||
params[0].getJsExecutorFactory().create(
|
||||
mJSCConfig == null ? new WritableNativeMap() : mJSCConfig.getConfigMap());
|
||||
return Result.of(createReactContext(jsExecutor, params[0].getJsBundleLoader()));
|
||||
} catch (Exception e) {
|
||||
// Pass exception to onPostExecute() so it can be handled on the main thread
|
||||
return Result.of(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Result<ReactApplicationContext> result) {
|
||||
try {
|
||||
setupReactContext(result.get());
|
||||
} catch (Exception e) {
|
||||
mDevSupportManager.handleException(e);
|
||||
} finally {
|
||||
mReactContextInitAsyncTask = null;
|
||||
}
|
||||
|
||||
// Handle enqueued request to re-initialize react context.
|
||||
if (mPendingReactContextInitParams != null) {
|
||||
recreateReactContextInBackground(
|
||||
mPendingReactContextInitParams.getJsExecutorFactory(),
|
||||
mPendingReactContextInitParams.getJsBundleLoader());
|
||||
mPendingReactContextInitParams = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelled(Result<ReactApplicationContext> reactApplicationContextResult) {
|
||||
try {
|
||||
mMemoryPressureRouter.destroy(reactApplicationContextResult.get());
|
||||
} catch (Exception e) {
|
||||
FLog.w(ReactConstants.TAG, "Caught exception after cancelling react context init", e);
|
||||
} finally {
|
||||
mReactContextInitAsyncTask = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Result<T> {
|
||||
@Nullable private final T mResult;
|
||||
@Nullable private final Exception mException;
|
||||
|
||||
public static <T, U extends T> Result<T> of(U result) {
|
||||
return new Result<T>(result);
|
||||
}
|
||||
|
||||
public static <T> Result<T> of(Exception exception) {
|
||||
return new Result<>(exception);
|
||||
}
|
||||
|
||||
private Result(T result) {
|
||||
mException = null;
|
||||
mResult = result;
|
||||
}
|
||||
|
||||
private Result(Exception exception) {
|
||||
mException = exception;
|
||||
mResult = null;
|
||||
}
|
||||
|
||||
public T get() throws Exception {
|
||||
if (mException != null) {
|
||||
throw mException;
|
||||
}
|
||||
|
||||
Assertions.assertNotNull(mResult);
|
||||
|
||||
return mResult;
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ ReactInstanceManagerImpl(
|
||||
Context applicationContext,
|
||||
@Nullable Activity currentActivity,
|
||||
@Nullable DefaultHardwareBackBtnHandler defaultHardwareBackBtnHandler,
|
||||
@Nullable String jsBundleFile,
|
||||
@Nullable String jsMainModuleName,
|
||||
List<ReactPackage> packages,
|
||||
boolean useDeveloperSupport,
|
||||
@Nullable NotThreadSafeBridgeIdleDebugListener bridgeIdleDebugListener,
|
||||
LifecycleState initialLifecycleState,
|
||||
UIImplementationProvider uiImplementationProvider,
|
||||
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler,
|
||||
@Nullable JSCConfig jscConfig) {
|
||||
|
||||
this(applicationContext,
|
||||
currentActivity,
|
||||
defaultHardwareBackBtnHandler,
|
||||
jsBundleFile,
|
||||
jsMainModuleName,
|
||||
packages,
|
||||
useDeveloperSupport,
|
||||
bridgeIdleDebugListener,
|
||||
initialLifecycleState,
|
||||
uiImplementationProvider,
|
||||
nativeModuleCallExceptionHandler,
|
||||
jscConfig,
|
||||
null);
|
||||
}
|
||||
|
||||
/* package */ ReactInstanceManagerImpl(
|
||||
Context applicationContext,
|
||||
@Nullable Activity currentActivity,
|
||||
@Nullable DefaultHardwareBackBtnHandler defaultHardwareBackBtnHandler,
|
||||
@Nullable String jsBundleFile,
|
||||
@Nullable String jsMainModuleName,
|
||||
List<ReactPackage> packages,
|
||||
boolean useDeveloperSupport,
|
||||
@Nullable NotThreadSafeBridgeIdleDebugListener bridgeIdleDebugListener,
|
||||
LifecycleState initialLifecycleState,
|
||||
UIImplementationProvider uiImplementationProvider,
|
||||
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler,
|
||||
@Nullable JSCConfig jscConfig,
|
||||
@Nullable RedBoxHandler redBoxHandler) {
|
||||
initializeSoLoaderIfNecessary(applicationContext);
|
||||
|
||||
// TODO(9577825): remove this
|
||||
ApplicationHolder.setApplication((Application) applicationContext.getApplicationContext());
|
||||
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(applicationContext);
|
||||
|
||||
mApplicationContext = applicationContext;
|
||||
mCurrentActivity = currentActivity;
|
||||
mDefaultBackButtonImpl = defaultHardwareBackBtnHandler;
|
||||
mJSBundleFile = jsBundleFile;
|
||||
mJSMainModuleName = jsMainModuleName;
|
||||
mPackages = packages;
|
||||
mUseDeveloperSupport = useDeveloperSupport;
|
||||
mRedBoxHandler = redBoxHandler;
|
||||
mDevSupportManager = DevSupportManagerFactory.create(
|
||||
applicationContext,
|
||||
mDevInterface,
|
||||
mJSMainModuleName,
|
||||
useDeveloperSupport,
|
||||
mRedBoxHandler);
|
||||
mBridgeIdleDebugListener = bridgeIdleDebugListener;
|
||||
mLifecycleState = initialLifecycleState;
|
||||
mUIImplementationProvider = uiImplementationProvider;
|
||||
mMemoryPressureRouter = new MemoryPressureRouter(applicationContext);
|
||||
mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
|
||||
mJSCConfig = jscConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DevSupportManager getDevSupportManager() {
|
||||
return mDevSupportManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryPressureRouter getMemoryPressureRouter() {
|
||||
return mMemoryPressureRouter;
|
||||
}
|
||||
|
||||
private static void initializeSoLoaderIfNecessary(Context applicationContext) {
|
||||
// Call SoLoader.initialize here, this is required for apps that does not use exopackage and
|
||||
// does not use SoLoader for loading other native code except from the one used by React Native
|
||||
// This way we don't need to require others to have additional initialization code and to
|
||||
// subclass android.app.Application.
|
||||
|
||||
// Method SoLoader.init is idempotent, so if you wish to use native exopackage, just call
|
||||
// SoLoader.init with appropriate args before initializing ReactInstanceManagerImpl
|
||||
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. 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.
|
||||
*/
|
||||
@Override
|
||||
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 (mDevSupportManager.hasUpToDateJSBundleInCache()) {
|
||||
// If there is a up-to-date bundle downloaded from server, always use that
|
||||
onJSBundleLoadedFromServer();
|
||||
} else if (mJSBundleFile == null) {
|
||||
mDevSupportManager.handleReloadJS();
|
||||
} else {
|
||||
mDevSupportManager.isPackagerRunning(
|
||||
new DevServerHelper.PackagerStatusCallback() {
|
||||
@Override
|
||||
public void onPackagerStatusFetched(final boolean packagerIsRunning) {
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (packagerIsRunning) {
|
||||
mDevSupportManager.handleReloadJS();
|
||||
} else {
|
||||
recreateReactContextInBackgroundFromBundleFile();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
recreateReactContextInBackgroundFromBundleFile();
|
||||
}
|
||||
|
||||
private void recreateReactContextInBackgroundFromBundleFile() {
|
||||
recreateReactContextInBackground(
|
||||
new JSCJavaScriptExecutor.Factory(),
|
||||
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
|
||||
* consume the event, mDefaultBackButtonImpl will be invoked at the end of the round trip to JS.
|
||||
*/
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
ReactContext reactContext = mCurrentReactContext;
|
||||
if (mCurrentReactContext == null) {
|
||||
// Invoke without round trip to JS.
|
||||
FLog.w(ReactConstants.TAG, "Instance detached from instance manager");
|
||||
invokeDefaultOnBackPressed();
|
||||
} else {
|
||||
DeviceEventManagerModule deviceEventManagerModule =
|
||||
Assertions.assertNotNull(reactContext).getNativeModule(DeviceEventManagerModule.class);
|
||||
deviceEventManagerModule.emitHardwareBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
private void invokeDefaultOnBackPressed() {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
if (mDefaultBackButtonImpl != null) {
|
||||
mDefaultBackButtonImpl.invokeDefaultOnBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
if (mCurrentReactContext == null) {
|
||||
FLog.w(ReactConstants.TAG, "Instance detached from instance manager");
|
||||
} else {
|
||||
String action = intent.getAction();
|
||||
Uri uri = intent.getData();
|
||||
|
||||
if (Intent.ACTION_VIEW.equals(action) && uri != null) {
|
||||
DeviceEventManagerModule deviceEventManagerModule =
|
||||
Assertions.assertNotNull(mCurrentReactContext).getNativeModule(DeviceEventManagerModule.class);
|
||||
deviceEventManagerModule.emitNewIntentReceived(uri);
|
||||
}
|
||||
|
||||
mCurrentReactContext.onNewIntent(mCurrentActivity, intent);
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleElementInspector() {
|
||||
if (mCurrentReactContext != null) {
|
||||
mCurrentReactContext
|
||||
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
||||
.emit("toggleElementInspector", null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHostPause() {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
|
||||
mDefaultBackButtonImpl = null;
|
||||
if (mUseDeveloperSupport) {
|
||||
mDevSupportManager.setDevSupportEnabled(false);
|
||||
}
|
||||
|
||||
moveToBeforeResumeLifecycleState();
|
||||
mCurrentActivity = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method when the activity resumes to enable invoking the back button directly from JS.
|
||||
*
|
||||
* This method retains an instance to provided mDefaultBackButtonImpl. Thus it's
|
||||
* important to pass from the activity instance that owns this particular instance of {@link
|
||||
* ReactInstanceManagerImpl}, so that once this instance receive {@link #onHostDestroy} event it will
|
||||
* clear the reference to that defaultBackButtonImpl.
|
||||
*
|
||||
* @param defaultBackButtonImpl a {@link DefaultHardwareBackBtnHandler} from an Activity that owns
|
||||
* this instance of {@link ReactInstanceManagerImpl}.
|
||||
*/
|
||||
@Override
|
||||
public void onHostResume(Activity activity, DefaultHardwareBackBtnHandler defaultBackButtonImpl) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
|
||||
mDefaultBackButtonImpl = defaultBackButtonImpl;
|
||||
if (mUseDeveloperSupport) {
|
||||
mDevSupportManager.setDevSupportEnabled(true);
|
||||
}
|
||||
|
||||
mCurrentActivity = activity;
|
||||
moveToResumedLifecycleState(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHostDestroy() {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
|
||||
if (mUseDeveloperSupport) {
|
||||
mDevSupportManager.setDevSupportEnabled(false);
|
||||
}
|
||||
|
||||
moveToBeforeCreateLifecycleState();
|
||||
mCurrentActivity = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
|
||||
if (mUseDeveloperSupport) {
|
||||
mDevSupportManager.setDevSupportEnabled(false);
|
||||
}
|
||||
|
||||
moveToBeforeCreateLifecycleState();
|
||||
|
||||
if (mReactContextInitAsyncTask != null) {
|
||||
mReactContextInitAsyncTask.cancel(true);
|
||||
}
|
||||
|
||||
mMemoryPressureRouter.destroy(mApplicationContext);
|
||||
|
||||
if (mCurrentReactContext != null) {
|
||||
mCurrentReactContext.destroy();
|
||||
mCurrentReactContext = null;
|
||||
mHasStartedCreatingInitialContext = false;
|
||||
}
|
||||
mCurrentActivity = null;
|
||||
|
||||
ResourceDrawableIdHelper.getInstance().clear();
|
||||
}
|
||||
|
||||
private void moveToResumedLifecycleState(boolean force) {
|
||||
if (mCurrentReactContext != null) {
|
||||
// we currently don't have an onCreate callback so we call onResume for both transitions
|
||||
if (force ||
|
||||
mLifecycleState == LifecycleState.BEFORE_RESUME ||
|
||||
mLifecycleState == LifecycleState.BEFORE_CREATE) {
|
||||
mCurrentReactContext.onHostResume(mCurrentActivity);
|
||||
}
|
||||
}
|
||||
mLifecycleState = LifecycleState.RESUMED;
|
||||
}
|
||||
|
||||
private void moveToBeforeResumeLifecycleState() {
|
||||
if (mCurrentReactContext != null) {
|
||||
if (mLifecycleState == LifecycleState.BEFORE_CREATE) {
|
||||
mCurrentReactContext.onHostResume(mCurrentActivity);
|
||||
mCurrentReactContext.onHostPause();
|
||||
} else if (mLifecycleState == LifecycleState.RESUMED) {
|
||||
mCurrentReactContext.onHostPause();
|
||||
}
|
||||
}
|
||||
mLifecycleState = LifecycleState.BEFORE_RESUME;
|
||||
}
|
||||
|
||||
private void moveToBeforeCreateLifecycleState() {
|
||||
if (mCurrentReactContext != null) {
|
||||
if (mLifecycleState == LifecycleState.RESUMED) {
|
||||
mCurrentReactContext.onHostPause();
|
||||
mLifecycleState = LifecycleState.BEFORE_RESUME;
|
||||
}
|
||||
if (mLifecycleState == LifecycleState.BEFORE_RESUME) {
|
||||
mCurrentReactContext.onHostDestroy();
|
||||
}
|
||||
}
|
||||
mLifecycleState = LifecycleState.BEFORE_CREATE;
|
||||
}
|
||||
|
||||
public LifecycleState getLifecycleState() {
|
||||
return mLifecycleState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (mCurrentReactContext != null) {
|
||||
mCurrentReactContext.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showDevOptionsDialog() {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
mDevSupportManager.showDevOptionsDialog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL where the last bundle was loaded from.
|
||||
*/
|
||||
@Override
|
||||
public String getSourceUrl() {
|
||||
return Assertions.assertNotNull(mSourceUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getJSBundleFile() {
|
||||
return mJSBundleFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach given {@param rootView} 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
|
||||
* be re-attached.
|
||||
*/
|
||||
@Override
|
||||
public void attachMeasuredRootView(ReactRootView rootView) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
mAttachedRootViews.add(rootView);
|
||||
|
||||
// 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 (mReactContextInitAsyncTask == null && mCurrentReactContext != null) {
|
||||
attachMeasuredRootViewToInstance(rootView, mCurrentReactContext.getCatalystInstance());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* first call.
|
||||
*/
|
||||
@Override
|
||||
public void detachRootView(ReactRootView rootView) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
if (mAttachedRootViews.remove(rootView)) {
|
||||
if (mCurrentReactContext != null && mCurrentReactContext.hasActiveCatalystInstance()) {
|
||||
detachViewFromInstance(rootView, mCurrentReactContext.getCatalystInstance());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses configured {@link ReactPackage} instances to create all view managers
|
||||
*/
|
||||
@Override
|
||||
public List<ViewManager> createAllViewManagers(
|
||||
ReactApplicationContext catalystApplicationContext) {
|
||||
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "createAllViewManagers");
|
||||
try {
|
||||
List<ViewManager> allViewManagers = new ArrayList<>();
|
||||
for (ReactPackage reactPackage : mPackages) {
|
||||
allViewManagers.addAll(reactPackage.createViewManagers(catalystApplicationContext));
|
||||
}
|
||||
return allViewManagers;
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addReactInstanceEventListener(ReactInstanceEventListener listener) {
|
||||
mReactInstanceEventListeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeReactInstanceEventListener(ReactInstanceEventListener listener) {
|
||||
mReactInstanceEventListeners.remove(listener);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Override
|
||||
public @Nullable ReactContext getCurrentReactContext() {
|
||||
return mCurrentReactContext;
|
||||
}
|
||||
|
||||
private void onReloadWithJSDebugger(JavaJSExecutor.Factory jsExecutorFactory) {
|
||||
recreateReactContextInBackground(
|
||||
new ProxyJavaScriptExecutor.Factory(jsExecutorFactory),
|
||||
JSBundleLoader.createRemoteDebuggerBundleLoader(
|
||||
mDevSupportManager.getJSBundleURLForRemoteDebugging(),
|
||||
mDevSupportManager.getSourceUrl()));
|
||||
}
|
||||
|
||||
private void onJSBundleLoadedFromServer() {
|
||||
recreateReactContextInBackground(
|
||||
new JSCJavaScriptExecutor.Factory(),
|
||||
JSBundleLoader.createCachedBundleFromNetworkLoader(
|
||||
mDevSupportManager.getSourceUrl(),
|
||||
mDevSupportManager.getDownloadedJSBundleFile()));
|
||||
}
|
||||
|
||||
private void recreateReactContextInBackground(
|
||||
JavaScriptExecutor.Factory jsExecutorFactory,
|
||||
JSBundleLoader jsBundleLoader) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
|
||||
ReactContextInitParams initParams =
|
||||
new ReactContextInitParams(jsExecutorFactory, jsBundleLoader);
|
||||
if (mReactContextInitAsyncTask == null) {
|
||||
// No background task to create react context is currently running, create and execute one.
|
||||
mReactContextInitAsyncTask = new ReactContextInitAsyncTask();
|
||||
mReactContextInitAsyncTask.execute(initParams);
|
||||
} else {
|
||||
// Background task is currently running, queue up most recent init params to recreate context
|
||||
// once task completes.
|
||||
mPendingReactContextInitParams = initParams;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
mMemoryPressureRouter.addMemoryPressureListener(catalystInstance);
|
||||
moveReactContextToCurrentLifecycleState();
|
||||
|
||||
for (ReactRootView rootView : mAttachedRootViews) {
|
||||
attachMeasuredRootViewToInstance(rootView, catalystInstance);
|
||||
}
|
||||
|
||||
ReactInstanceEventListener[] listeners =
|
||||
new ReactInstanceEventListener[mReactInstanceEventListeners.size()];
|
||||
listeners = mReactInstanceEventListeners.toArray(listeners);
|
||||
|
||||
for (ReactInstanceEventListener listener : listeners) {
|
||||
listener.onReactContextInitialized(reactContext);
|
||||
}
|
||||
}
|
||||
|
||||
private void attachMeasuredRootViewToInstance(
|
||||
ReactRootView rootView,
|
||||
CatalystInstance catalystInstance) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
|
||||
// Reset view content as it's going to be populated by the application content from JS
|
||||
rootView.removeAllViews();
|
||||
rootView.setId(View.NO_ID);
|
||||
|
||||
UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class);
|
||||
int rootTag = uiManagerModule.addMeasuredRootView(rootView);
|
||||
rootView.setRootViewTag(rootTag);
|
||||
@Nullable Bundle launchOptions = rootView.getLaunchOptions();
|
||||
WritableMap initialProps = launchOptions != null
|
||||
? Arguments.fromBundle(launchOptions)
|
||||
: Arguments.createMap();
|
||||
String jsAppModuleName = rootView.getJSModuleName();
|
||||
|
||||
WritableNativeMap appParams = new WritableNativeMap();
|
||||
appParams.putDouble("rootTag", rootTag);
|
||||
appParams.putMap("initialProps", initialProps);
|
||||
catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
|
||||
}
|
||||
|
||||
private void detachViewFromInstance(
|
||||
ReactRootView rootView,
|
||||
CatalystInstance catalystInstance) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
catalystInstance.getJSModule(AppRegistry.class)
|
||||
.unmountApplicationComponentAtRootTag(rootView.getId());
|
||||
}
|
||||
|
||||
private void tearDownReactContext(ReactContext reactContext) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
if (mLifecycleState == LifecycleState.RESUMED) {
|
||||
reactContext.onHostPause();
|
||||
}
|
||||
for (ReactRootView rootView : mAttachedRootViews) {
|
||||
detachViewFromInstance(rootView, reactContext.getCatalystInstance());
|
||||
}
|
||||
reactContext.destroy();
|
||||
mDevSupportManager.onReactInstanceDestroyed(reactContext);
|
||||
mMemoryPressureRouter.removeMemoryPressureListener(reactContext.getCatalystInstance());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return instance of {@link ReactContext} configured a {@link CatalystInstance} set
|
||||
*/
|
||||
private ReactApplicationContext createReactContext(
|
||||
JavaScriptExecutor jsExecutor,
|
||||
JSBundleLoader jsBundleLoader) {
|
||||
FLog.i(ReactConstants.TAG, "Creating react context.");
|
||||
ReactMarker.logMarker(CREATE_REACT_CONTEXT_START);
|
||||
// CREATE_REACT_CONTEXT_END is in JSCExecutor.cpp
|
||||
mSourceUrl = jsBundleLoader.getSourceUrl();
|
||||
NativeModuleRegistry.Builder nativeRegistryBuilder = new NativeModuleRegistry.Builder();
|
||||
JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder();
|
||||
|
||||
final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
|
||||
if (mUseDeveloperSupport) {
|
||||
reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);
|
||||
}
|
||||
|
||||
ReactMarker.logMarker(PROCESS_PACKAGES_START);
|
||||
Systrace.beginSection(
|
||||
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
|
||||
"createAndProcessCoreModulesPackage");
|
||||
try {
|
||||
CoreModulesPackage coreModulesPackage =
|
||||
new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);
|
||||
processPackage(coreModulesPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
}
|
||||
|
||||
// TODO(6818138): Solve use-case of native/js modules overriding
|
||||
for (ReactPackage reactPackage : mPackages) {
|
||||
Systrace.beginSection(
|
||||
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
|
||||
"createAndProcessCustomReactPackage");
|
||||
try {
|
||||
processPackage(reactPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
}
|
||||
}
|
||||
ReactMarker.logMarker(PROCESS_PACKAGES_END);
|
||||
|
||||
ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_START);
|
||||
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "buildNativeModuleRegistry");
|
||||
NativeModuleRegistry nativeModuleRegistry;
|
||||
try {
|
||||
nativeModuleRegistry = nativeRegistryBuilder.build();
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_END);
|
||||
}
|
||||
|
||||
NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null
|
||||
? mNativeModuleCallExceptionHandler
|
||||
: mDevSupportManager;
|
||||
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
|
||||
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
|
||||
.setJSExecutor(jsExecutor)
|
||||
.setRegistry(nativeModuleRegistry)
|
||||
.setJSModuleRegistry(jsModulesBuilder.build())
|
||||
.setJSBundleLoader(jsBundleLoader)
|
||||
.setNativeModuleCallExceptionHandler(exceptionHandler);
|
||||
|
||||
ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_START);
|
||||
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance");
|
||||
final CatalystInstance catalystInstance;
|
||||
try {
|
||||
catalystInstance = catalystInstanceBuilder.build();
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_END);
|
||||
}
|
||||
|
||||
if (mBridgeIdleDebugListener != null) {
|
||||
catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
|
||||
}
|
||||
|
||||
try {
|
||||
catalystInstance.getReactQueueConfiguration().getJSQueueThread().callOnQueue(
|
||||
new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() {
|
||||
// We want to ensure that any code that checks ReactContext#hasActiveCatalystInstance
|
||||
// can be sure that it's safe to call a JS module function. As JS module function calls
|
||||
// execute on the JS thread, and this Runnable runs on the JS thread, at this point we
|
||||
// know that no JS module function calls will be executed until after this Runnable completes.
|
||||
//
|
||||
// This means it is now safe to say the instance is initialized.
|
||||
//
|
||||
// The reason we call this here instead of after this Runnable completes is so that we can
|
||||
// reduce the amount of time until the React instance is able to start accepting JS calls,
|
||||
// and so that any native module calls that result from runJSBundle can access JS modules.
|
||||
reactContext.initializeWithInstance(catalystInstance);
|
||||
|
||||
ReactMarker.logMarker(RUN_JS_BUNDLE_START);
|
||||
// RUN_JS_BUNDLE_END is in JSCExecutor.cpp
|
||||
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "runJSBundle");
|
||||
try {
|
||||
catalystInstance.runJSBundle();
|
||||
} finally {
|
||||
// This will actually finish when `JSCExecutor#loadApplicationScript()` finishes
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}).get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return reactContext;
|
||||
}
|
||||
|
||||
private void processPackage(
|
||||
ReactPackage reactPackage,
|
||||
ReactApplicationContext reactContext,
|
||||
NativeModuleRegistry.Builder nativeRegistryBuilder,
|
||||
JavaScriptModuleRegistry.Builder jsModulesBuilder) {
|
||||
for (NativeModule nativeModule : reactPackage.createNativeModules(reactContext)) {
|
||||
nativeRegistryBuilder.add(nativeModule);
|
||||
}
|
||||
for (Class<? extends JavaScriptModule> jsModuleClass : reactPackage.createJSModules()) {
|
||||
jsModulesBuilder.add(jsModuleClass);
|
||||
}
|
||||
}
|
||||
|
||||
private void moveReactContextToCurrentLifecycleState() {
|
||||
if (mLifecycleState == LifecycleState.RESUMED) {
|
||||
moveToResumedLifecycleState(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,568 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.bridge;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
import com.facebook.react.bridge.queue.ReactQueueConfiguration;
|
||||
import com.facebook.react.bridge.queue.ReactQueueConfigurationImpl;
|
||||
import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec;
|
||||
import com.facebook.react.bridge.queue.QueueThreadExceptionHandler;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.common.annotations.VisibleForTesting;
|
||||
import com.facebook.react.common.futures.SimpleSettableFuture;
|
||||
import com.facebook.systrace.Systrace;
|
||||
import com.facebook.systrace.TraceListener;
|
||||
|
||||
/**
|
||||
* This provides an implementation of the public CatalystInstance instance. It is public because
|
||||
* it is built by ReactInstanceManager which is in a different package.
|
||||
*/
|
||||
@DoNotStrip
|
||||
public class CatalystInstanceImpl implements CatalystInstance {
|
||||
|
||||
private static final AtomicInteger sNextInstanceIdForTrace = new AtomicInteger(1);
|
||||
|
||||
// Access from any thread
|
||||
private final ReactQueueConfigurationImpl mReactQueueConfiguration;
|
||||
private final CopyOnWriteArrayList<NotThreadSafeBridgeIdleDebugListener> mBridgeIdleListeners;
|
||||
private final AtomicInteger mPendingJSCalls = new AtomicInteger(0);
|
||||
private final String mJsPendingCallsTitleForTrace =
|
||||
"pending_js_calls_instance" + sNextInstanceIdForTrace.getAndIncrement();
|
||||
private volatile boolean mIsBeingDestroyed = false;
|
||||
private volatile boolean mDestroyed = false;
|
||||
private final TraceListener mTraceListener;
|
||||
private final JavaScriptModuleRegistry mJSModuleRegistry;
|
||||
private final JSBundleLoader mJSBundleLoader;
|
||||
private @Nullable ExecutorToken mMainExecutorToken;
|
||||
|
||||
// These locks prevent additional calls from going JS<->Java after the bridge has been torn down.
|
||||
// There are separate ones for each direction because a JS to Java call can trigger a Java to JS
|
||||
// call: this would cause a deadlock with a traditional mutex (maybe we should be using a reader-
|
||||
// writer lock but then we'd have to worry about starving the destroy call).
|
||||
private final Object mJSToJavaCallsTeardownLock = new Object();
|
||||
private final Object mJavaToJSCallsTeardownLock = new Object();
|
||||
|
||||
// Access from native modules thread
|
||||
private final NativeModuleRegistry mJavaRegistry;
|
||||
private final NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler;
|
||||
private boolean mInitialized = false;
|
||||
|
||||
// Access from JS thread
|
||||
private final ReactBridge mBridge;
|
||||
private boolean mJSBundleHasLoaded;
|
||||
|
||||
private CatalystInstanceImpl(
|
||||
final ReactQueueConfigurationSpec ReactQueueConfigurationSpec,
|
||||
final JavaScriptExecutor jsExecutor,
|
||||
final NativeModuleRegistry registry,
|
||||
final JavaScriptModuleRegistry jsModuleRegistry,
|
||||
final JSBundleLoader jsBundleLoader,
|
||||
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
|
||||
FLog.d(ReactConstants.TAG, "Initializing React Bridge.");
|
||||
mReactQueueConfiguration = ReactQueueConfigurationImpl.create(
|
||||
ReactQueueConfigurationSpec,
|
||||
new NativeExceptionHandler());
|
||||
mBridgeIdleListeners = new CopyOnWriteArrayList<>();
|
||||
mJavaRegistry = registry;
|
||||
mJSModuleRegistry = jsModuleRegistry;
|
||||
mJSBundleLoader = jsBundleLoader;
|
||||
mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
|
||||
mTraceListener = new JSProfilerTraceListener();
|
||||
|
||||
try {
|
||||
mBridge = mReactQueueConfiguration.getJSQueueThread().callOnQueue(
|
||||
new Callable<ReactBridge>() {
|
||||
@Override
|
||||
public ReactBridge call() throws Exception {
|
||||
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "initializeBridge");
|
||||
try {
|
||||
return initializeBridge(jsExecutor);
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
}
|
||||
}
|
||||
}).get();
|
||||
} catch (Exception t) {
|
||||
throw new RuntimeException("Failed to initialize bridge", t);
|
||||
}
|
||||
}
|
||||
|
||||
private ReactBridge initializeBridge(JavaScriptExecutor jsExecutor) {
|
||||
mReactQueueConfiguration.getJSQueueThread().assertIsOnThread();
|
||||
Assertions.assertCondition(mBridge == null, "initializeBridge should be called once");
|
||||
|
||||
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "ReactBridgeCtor");
|
||||
ReactBridge bridge;
|
||||
try {
|
||||
bridge = new ReactBridge(
|
||||
jsExecutor,
|
||||
new NativeModulesReactCallback(),
|
||||
mReactQueueConfiguration.getNativeModulesQueueThread());
|
||||
mMainExecutorToken = bridge.getMainExecutorToken();
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
}
|
||||
|
||||
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "setBatchedBridgeConfig");
|
||||
try {
|
||||
bridge.setGlobalVariable(
|
||||
"__fbBatchedBridgeConfig",
|
||||
buildModulesConfigJSONProperty(mJavaRegistry));
|
||||
bridge.setGlobalVariable(
|
||||
"__RCTProfileIsProfiling",
|
||||
Systrace.isTracing(Systrace.TRACE_TAG_REACT_APPS) ? "true" : "false");
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
}
|
||||
|
||||
mJavaRegistry.notifyReactBridgeInitialized(bridge);
|
||||
return bridge;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runJSBundle() {
|
||||
mReactQueueConfiguration.getJSQueueThread().assertIsOnThread();
|
||||
Assertions.assertCondition(!mJSBundleHasLoaded, "JS bundle was already loaded!");
|
||||
|
||||
incrementPendingJSCalls();
|
||||
|
||||
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "loadJSScript");
|
||||
try {
|
||||
mJSBundleLoader.loadScript(mBridge);
|
||||
|
||||
// This is registered after JS starts since it makes a JS call
|
||||
Systrace.registerListener(mTraceListener);
|
||||
} catch (JSExecutionException e) {
|
||||
mNativeModuleCallExceptionHandler.handleException(e);
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
}
|
||||
|
||||
mJSBundleHasLoaded = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void callFunction(
|
||||
ExecutorToken executorToken,
|
||||
String module,
|
||||
String method,
|
||||
NativeArray arguments) {
|
||||
if (mIsBeingDestroyed) {
|
||||
FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed.");
|
||||
return;
|
||||
}
|
||||
synchronized (mJavaToJSCallsTeardownLock) {
|
||||
if (mDestroyed) {
|
||||
FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed.");
|
||||
return;
|
||||
}
|
||||
|
||||
incrementPendingJSCalls();
|
||||
|
||||
Assertions.assertNotNull(mBridge).callFunction(
|
||||
executorToken,
|
||||
module,
|
||||
method, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
// This is called from java code, so it won't be stripped anyway, but proguard will rename it,
|
||||
// which this prevents.
|
||||
@DoNotStrip
|
||||
@Override
|
||||
public void invokeCallback(ExecutorToken executorToken, int callbackID, NativeArray arguments) {
|
||||
if (mIsBeingDestroyed) {
|
||||
FLog.w(ReactConstants.TAG, "Invoking JS callback after bridge has been destroyed.");
|
||||
return;
|
||||
}
|
||||
synchronized (mJavaToJSCallsTeardownLock) {
|
||||
if (mDestroyed) {
|
||||
FLog.w(ReactConstants.TAG, "Invoking JS callback after bridge has been destroyed.");
|
||||
return;
|
||||
}
|
||||
|
||||
incrementPendingJSCalls();
|
||||
|
||||
Assertions.assertNotNull(mBridge).invokeCallback(executorToken, callbackID, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys this catalyst instance, waiting for any other threads in ReactQueueConfiguration
|
||||
* (besides the UI thread) to finish running. Must be called from the UI thread so that we can
|
||||
* fully shut down other threads.
|
||||
*/
|
||||
@Override
|
||||
public void destroy() {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
|
||||
// This ordering is important. A JS to Java call that triggers a Java to JS call will also
|
||||
// acquire these locks in the same order
|
||||
mIsBeingDestroyed = true;
|
||||
synchronized (mJSToJavaCallsTeardownLock) {
|
||||
synchronized (mJavaToJSCallsTeardownLock) {
|
||||
if (mDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: tell all APIs to shut down
|
||||
mDestroyed = true;
|
||||
mJavaRegistry.notifyCatalystInstanceDestroy();
|
||||
|
||||
Systrace.unregisterListener(mTraceListener);
|
||||
|
||||
synchronouslyDisposeBridgeOnJSThread();
|
||||
}
|
||||
}
|
||||
|
||||
mReactQueueConfiguration.destroy();
|
||||
|
||||
boolean wasIdle = (mPendingJSCalls.getAndSet(0) == 0);
|
||||
if (!wasIdle && !mBridgeIdleListeners.isEmpty()) {
|
||||
for (NotThreadSafeBridgeIdleDebugListener listener : mBridgeIdleListeners) {
|
||||
listener.onTransitionToBridgeIdle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void synchronouslyDisposeBridgeOnJSThread() {
|
||||
final SimpleSettableFuture<Void> bridgeDisposeFuture = new SimpleSettableFuture<>();
|
||||
mReactQueueConfiguration.getJSQueueThread().runOnQueue(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mBridge.destroy();
|
||||
mBridge.dispose();
|
||||
bridgeDisposeFuture.set(null);
|
||||
}
|
||||
});
|
||||
bridgeDisposeFuture.getOrThrow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDestroyed() {
|
||||
return mDestroyed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all the native modules
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@Override
|
||||
public void initialize() {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
Assertions.assertCondition(
|
||||
!mInitialized,
|
||||
"This catalyst instance has already been initialized");
|
||||
mInitialized = true;
|
||||
mJavaRegistry.notifyCatalystInstanceInitialized();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactQueueConfiguration getReactQueueConfiguration() {
|
||||
return mReactQueueConfiguration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends JavaScriptModule> T getJSModule(Class<T> jsInterface) {
|
||||
return getJSModule(Assertions.assertNotNull(mMainExecutorToken), jsInterface);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends JavaScriptModule> T getJSModule(ExecutorToken executorToken, Class<T> jsInterface) {
|
||||
return Assertions.assertNotNull(mJSModuleRegistry)
|
||||
.getJavaScriptModule(this, executorToken, jsInterface);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends NativeModule> boolean hasNativeModule(Class<T> nativeModuleInterface) {
|
||||
return mJavaRegistry.hasModule(nativeModuleInterface);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends NativeModule> T getNativeModule(Class<T> nativeModuleInterface) {
|
||||
return mJavaRegistry.getModule(nativeModuleInterface);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<NativeModule> getNativeModules() {
|
||||
return mJavaRegistry.getAllModules();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMemoryPressure(final MemoryPressure level) {
|
||||
mReactQueueConfiguration.getJSQueueThread().runOnQueue(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Assertions.assertNotNull(mBridge).handleMemoryPressure(level);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a idle listener for this Catalyst instance. The listener will receive notifications
|
||||
* whenever the bridge transitions from idle to busy and vice-versa, where the busy state is
|
||||
* defined as there being some non-zero number of calls to JS that haven't resolved via a
|
||||
* onBatchCompleted call. The listener should be purely passive and not affect application logic.
|
||||
*/
|
||||
@Override
|
||||
public void addBridgeIdleDebugListener(NotThreadSafeBridgeIdleDebugListener listener) {
|
||||
mBridgeIdleListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a NotThreadSafeBridgeIdleDebugListener previously added with
|
||||
* {@link #addBridgeIdleDebugListener}
|
||||
*/
|
||||
@Override
|
||||
public void removeBridgeIdleDebugListener(NotThreadSafeBridgeIdleDebugListener listener) {
|
||||
mBridgeIdleListeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsProfiling() {
|
||||
return mBridge.supportsProfiling();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startProfiler(String title) {
|
||||
mBridge.startProfiler(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopProfiler(String title, String filename) {
|
||||
mBridge.stopProfiler(title, filename);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Override
|
||||
public void setGlobalVariable(String propName, String jsonValue) {
|
||||
mBridge.setGlobalVariable(propName, jsonValue);
|
||||
}
|
||||
|
||||
private String buildModulesConfigJSONProperty(NativeModuleRegistry nativeModuleRegistry) {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
JsonWriter writer = new JsonWriter(stringWriter);
|
||||
try {
|
||||
writer.beginObject();
|
||||
writer.name("remoteModuleConfig");
|
||||
nativeModuleRegistry.writeModuleDescriptions(writer);
|
||||
writer.endObject();
|
||||
return stringWriter.toString();
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException("Unable to serialize JavaScript module declaration", ioe);
|
||||
} finally {
|
||||
try {
|
||||
writer.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void incrementPendingJSCalls() {
|
||||
int oldPendingCalls = mPendingJSCalls.getAndIncrement();
|
||||
boolean wasIdle = oldPendingCalls == 0;
|
||||
Systrace.traceCounter(
|
||||
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
|
||||
mJsPendingCallsTitleForTrace,
|
||||
oldPendingCalls + 1);
|
||||
if (wasIdle && !mBridgeIdleListeners.isEmpty()) {
|
||||
for (NotThreadSafeBridgeIdleDebugListener listener : mBridgeIdleListeners) {
|
||||
listener.onTransitionToBridgeBusy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void decrementPendingJSCalls() {
|
||||
int newPendingCalls = mPendingJSCalls.decrementAndGet();
|
||||
// TODO(9604406): handle case of web workers injecting messages to main thread
|
||||
//Assertions.assertCondition(newPendingCalls >= 0);
|
||||
boolean isNowIdle = newPendingCalls == 0;
|
||||
Systrace.traceCounter(
|
||||
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
|
||||
mJsPendingCallsTitleForTrace,
|
||||
newPendingCalls);
|
||||
|
||||
if (isNowIdle && !mBridgeIdleListeners.isEmpty()) {
|
||||
for (NotThreadSafeBridgeIdleDebugListener listener : mBridgeIdleListeners) {
|
||||
listener.onTransitionToBridgeIdle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
Assertions.assertCondition(mDestroyed, "Bridge was not destroyed before finalizer!");
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
private class NativeModulesReactCallback implements ReactCallback {
|
||||
|
||||
@Override
|
||||
public void call(ExecutorToken executorToken, int moduleId, int methodId, ReadableNativeArray parameters) {
|
||||
mReactQueueConfiguration.getNativeModulesQueueThread().assertIsOnThread();
|
||||
|
||||
if (mIsBeingDestroyed) {
|
||||
return;
|
||||
}
|
||||
synchronized (mJSToJavaCallsTeardownLock) {
|
||||
// Suppress any callbacks if destroyed - will only lead to sadness.
|
||||
if (mDestroyed) {
|
||||
return;
|
||||
}
|
||||
mJavaRegistry.call(CatalystInstanceImpl.this, executorToken, moduleId, methodId, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBatchComplete() {
|
||||
mReactQueueConfiguration.getNativeModulesQueueThread().assertIsOnThread();
|
||||
|
||||
// The bridge may have been destroyed due to an exception during the batch. In that case
|
||||
// native modules could be in a bad state so we don't want to call anything on them. We
|
||||
// still want to trigger the debug listener since it allows instrumentation tests to end and
|
||||
// check their assertions without waiting for a timeout.
|
||||
if (mIsBeingDestroyed) {
|
||||
return;
|
||||
}
|
||||
synchronized (mJSToJavaCallsTeardownLock) {
|
||||
if (mDestroyed) {
|
||||
return;
|
||||
}
|
||||
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "onBatchComplete");
|
||||
try {
|
||||
mJavaRegistry.onBatchComplete();
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
}
|
||||
}
|
||||
|
||||
decrementPendingJSCalls();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExecutorUnregistered(ExecutorToken executorToken) {
|
||||
mReactQueueConfiguration.getNativeModulesQueueThread().assertIsOnThread();
|
||||
|
||||
// Since onCatalystInstanceDestroy happens on the UI thread, we don't want to also execute
|
||||
// this callback on the native modules thread at the same time. Longer term, onCatalystInstanceDestroy
|
||||
// should probably be executed on the native modules thread as well instead.
|
||||
if (mIsBeingDestroyed) {
|
||||
return;
|
||||
}
|
||||
synchronized (mJSToJavaCallsTeardownLock) {
|
||||
if (mDestroyed) {
|
||||
return;
|
||||
}
|
||||
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "onExecutorUnregistered");
|
||||
try {
|
||||
mJavaRegistry.onExecutorUnregistered(executorToken);
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class NativeExceptionHandler implements QueueThreadExceptionHandler {
|
||||
|
||||
@Override
|
||||
public void handleException(Exception e) {
|
||||
// Any Exception caught here is because of something in JS. Even if it's a bug in the
|
||||
// framework/native code, it was triggered by JS and theoretically since we were able
|
||||
// to set up the bridge, JS could change its logic, reload, and not trigger that crash.
|
||||
mNativeModuleCallExceptionHandler.handleException(e);
|
||||
mReactQueueConfiguration.getUIQueueThread().runOnQueue(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private class JSProfilerTraceListener implements TraceListener {
|
||||
@Override
|
||||
public void onTraceStarted() {
|
||||
getJSModule(Assertions.assertNotNull(mMainExecutorToken), com.facebook.react.bridge.Systrace.class).setEnabled(
|
||||
true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTraceStopped() {
|
||||
getJSModule(Assertions.assertNotNull(mMainExecutorToken), com.facebook.react.bridge.Systrace.class).setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private @Nullable ReactQueueConfigurationSpec mReactQueueConfigurationSpec;
|
||||
private @Nullable JSBundleLoader mJSBundleLoader;
|
||||
private @Nullable NativeModuleRegistry mRegistry;
|
||||
private @Nullable JavaScriptModuleRegistry mJSModuleRegistry;
|
||||
private @Nullable JavaScriptExecutor mJSExecutor;
|
||||
private @Nullable NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler;
|
||||
|
||||
public Builder setReactQueueConfigurationSpec(
|
||||
ReactQueueConfigurationSpec ReactQueueConfigurationSpec) {
|
||||
mReactQueueConfigurationSpec = ReactQueueConfigurationSpec;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRegistry(NativeModuleRegistry registry) {
|
||||
mRegistry = registry;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setJSModuleRegistry(JavaScriptModuleRegistry jsModuleRegistry) {
|
||||
mJSModuleRegistry = jsModuleRegistry;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setJSBundleLoader(JSBundleLoader jsBundleLoader) {
|
||||
mJSBundleLoader = jsBundleLoader;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setJSExecutor(JavaScriptExecutor jsExecutor) {
|
||||
mJSExecutor = jsExecutor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setNativeModuleCallExceptionHandler(
|
||||
NativeModuleCallExceptionHandler handler) {
|
||||
mNativeModuleCallExceptionHandler = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CatalystInstanceImpl build() {
|
||||
return new CatalystInstanceImpl(
|
||||
Assertions.assertNotNull(mReactQueueConfigurationSpec),
|
||||
Assertions.assertNotNull(mJSExecutor),
|
||||
Assertions.assertNotNull(mRegistry),
|
||||
Assertions.assertNotNull(mJSModuleRegistry),
|
||||
Assertions.assertNotNull(mJSBundleLoader),
|
||||
Assertions.assertNotNull(mNativeModuleCallExceptionHandler));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.bridge;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* A class that stores JS bundle information and allows {@link CatalystInstance} to load a correct
|
||||
* bundle through {@link ReactBridge}.
|
||||
*/
|
||||
public abstract class JSBundleLoader {
|
||||
|
||||
/**
|
||||
* This loader is recommended one for release version of your app. In that case local JS executor
|
||||
* should be used. JS bundle will be read from assets directory in native code to save on passing
|
||||
* large strings from java to native memory.
|
||||
*/
|
||||
public static JSBundleLoader createFileLoader(
|
||||
final Context context,
|
||||
final String fileName) {
|
||||
return new JSBundleLoader() {
|
||||
@Override
|
||||
public void loadScript(ReactBridge bridge) {
|
||||
if (fileName.startsWith("assets://")) {
|
||||
bridge.loadScriptFromAssets(context.getAssets(), fileName.replaceFirst("assets://", ""));
|
||||
} else {
|
||||
bridge.loadScriptFromFile(fileName, "file://" + fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSourceUrl() {
|
||||
return (fileName.startsWith("assets://") ? "" : "file://") + fileName;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This loader is used when bundle gets reloaded from dev server. In that case loader expect JS
|
||||
* bundle to be prefetched and stored in local file. We do that to avoid passing large strings
|
||||
* between java and native code and avoid allocating memory in java to fit whole JS bundle in it.
|
||||
* Providing correct {@param sourceURL} of downloaded bundle is required for JS stacktraces to
|
||||
* work correctly and allows for source maps to correctly symbolize those.
|
||||
*/
|
||||
public static JSBundleLoader createCachedBundleFromNetworkLoader(
|
||||
final String sourceURL,
|
||||
final String cachedFileLocation) {
|
||||
return new JSBundleLoader() {
|
||||
@Override
|
||||
public void loadScript(ReactBridge bridge) {
|
||||
bridge.loadScriptFromFile(cachedFileLocation, sourceURL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSourceUrl() {
|
||||
return sourceURL;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This loader is used when proxy debugging is enabled. In that case there is no point in fetching
|
||||
* the bundle from device as remote executor will have to do it anyway.
|
||||
*
|
||||
* @param proxySourceURL the URL to load the JS bundle from in the JavaScript proxy
|
||||
* @param realSourceURL the URL to report as the source URL, e.g. for asset loading
|
||||
*/
|
||||
public static JSBundleLoader createRemoteDebuggerBundleLoader(
|
||||
final String proxySourceURL,
|
||||
final String realSourceURL) {
|
||||
return new JSBundleLoader() {
|
||||
@Override
|
||||
public void loadScript(ReactBridge bridge) {
|
||||
bridge.loadScriptFromFile(null, proxySourceURL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSourceUrl() {
|
||||
return realSourceURL;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public abstract void loadScript(ReactBridge bridge);
|
||||
public abstract String getSourceUrl();
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.bridge;
|
||||
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
@DoNotStrip
|
||||
public class JSCJavaScriptExecutor extends JavaScriptExecutor {
|
||||
static {
|
||||
ReactBridge.staticInit();
|
||||
}
|
||||
|
||||
public static class Factory implements JavaScriptExecutor.Factory {
|
||||
@Override
|
||||
public JavaScriptExecutor create(WritableNativeMap jscConfig) throws Exception {
|
||||
return new JSCJavaScriptExecutor(jscConfig);
|
||||
}
|
||||
}
|
||||
|
||||
public JSCJavaScriptExecutor(WritableNativeMap jscConfig) {
|
||||
initialize(jscConfig);
|
||||
}
|
||||
|
||||
private native void initialize(WritableNativeMap jscConfig);
|
||||
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
package com.facebook.react.bridge;
|
||||
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
|
||||
/**
|
||||
* Exception thrown when there is an error evaluating JS, e.g. a syntax error.
|
||||
*/
|
||||
@DoNotStrip
|
||||
public class JSExecutionException extends RuntimeException {
|
||||
|
||||
@DoNotStrip
|
||||
public JSExecutionException(String detailMessage) {
|
||||
super(detailMessage);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.bridge;
|
||||
|
||||
import com.facebook.jni.Countable;
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
|
||||
@DoNotStrip
|
||||
public abstract class JavaScriptExecutor extends Countable {
|
||||
public interface Factory {
|
||||
JavaScriptExecutor create(WritableNativeMap jscConfig) throws Exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close this executor and cleanup any resources that it was using. No further calls are
|
||||
* expected after this.
|
||||
*/
|
||||
public void close() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,235 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.bridge;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.facebook.react.common.MapBuilder;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.systrace.Systrace;
|
||||
|
||||
/**
|
||||
* A set of Java APIs to expose to a particular JavaScript instance.
|
||||
*/
|
||||
public class NativeModuleRegistry {
|
||||
|
||||
private final List<ModuleDefinition> mModuleTable;
|
||||
private final Map<Class<? extends NativeModule>, NativeModule> mModuleInstances;
|
||||
private final ArrayList<OnBatchCompleteListener> mBatchCompleteListenerModules;
|
||||
private final ArrayList<OnExecutorUnregisteredListener> mOnExecutorUnregisteredListenerModules;
|
||||
|
||||
private NativeModuleRegistry(
|
||||
List<ModuleDefinition> moduleTable,
|
||||
Map<Class<? extends NativeModule>, NativeModule> moduleInstances) {
|
||||
mModuleTable = moduleTable;
|
||||
mModuleInstances = moduleInstances;
|
||||
|
||||
mBatchCompleteListenerModules = new ArrayList<>(mModuleTable.size());
|
||||
mOnExecutorUnregisteredListenerModules = new ArrayList<>(mModuleTable.size());
|
||||
for (int i = 0; i < mModuleTable.size(); i++) {
|
||||
ModuleDefinition definition = mModuleTable.get(i);
|
||||
if (definition.target instanceof OnBatchCompleteListener) {
|
||||
mBatchCompleteListenerModules.add((OnBatchCompleteListener) definition.target);
|
||||
}
|
||||
if (definition.target instanceof OnExecutorUnregisteredListener) {
|
||||
mOnExecutorUnregisteredListenerModules.add((OnExecutorUnregisteredListener) definition.target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ void call(
|
||||
CatalystInstance catalystInstance,
|
||||
ExecutorToken executorToken,
|
||||
int moduleId,
|
||||
int methodId,
|
||||
ReadableNativeArray parameters) {
|
||||
ModuleDefinition definition = mModuleTable.get(moduleId);
|
||||
if (definition == null) {
|
||||
throw new RuntimeException("Call to unknown module: " + moduleId);
|
||||
}
|
||||
definition.call(catalystInstance, executorToken, methodId, parameters);
|
||||
}
|
||||
|
||||
/* package */ void writeModuleDescriptions(JsonWriter writer) throws IOException {
|
||||
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "CreateJSON");
|
||||
try {
|
||||
writer.beginObject();
|
||||
for (ModuleDefinition moduleDef : mModuleTable) {
|
||||
writer.name(moduleDef.name).beginObject();
|
||||
writer.name("moduleID").value(moduleDef.id);
|
||||
writer.name("supportsWebWorkers").value(moduleDef.target.supportsWebWorkers());
|
||||
writer.name("methods").beginObject();
|
||||
for (int i = 0; i < moduleDef.methods.size(); i++) {
|
||||
MethodRegistration method = moduleDef.methods.get(i);
|
||||
writer.name(method.name).beginObject();
|
||||
writer.name("methodID").value(i);
|
||||
writer.name("type").value(method.method.getType());
|
||||
writer.endObject();
|
||||
}
|
||||
writer.endObject();
|
||||
moduleDef.target.writeConstantsField(writer, "constants");
|
||||
writer.endObject();
|
||||
}
|
||||
writer.endObject();
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ void notifyCatalystInstanceDestroy() {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
Systrace.beginSection(
|
||||
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
|
||||
"NativeModuleRegistry_notifyCatalystInstanceDestroy");
|
||||
try {
|
||||
for (NativeModule nativeModule : mModuleInstances.values()) {
|
||||
nativeModule.onCatalystInstanceDestroy();
|
||||
}
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ void notifyCatalystInstanceInitialized() {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
|
||||
ReactMarker.logMarker("NativeModule_start");
|
||||
Systrace.beginSection(
|
||||
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
|
||||
"NativeModuleRegistry_notifyCatalystInstanceInitialized");
|
||||
try {
|
||||
for (NativeModule nativeModule : mModuleInstances.values()) {
|
||||
nativeModule.initialize();
|
||||
}
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
ReactMarker.logMarker("NativeModule_end");
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ void notifyReactBridgeInitialized(ReactBridge bridge) {
|
||||
Systrace.beginSection(
|
||||
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
|
||||
"NativeModuleRegistry_notifyReactBridgeInitialized");
|
||||
try {
|
||||
for (NativeModule nativeModule : mModuleInstances.values()) {
|
||||
nativeModule.onReactBridgeInitialized(bridge);
|
||||
}
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
}
|
||||
}
|
||||
|
||||
public void onBatchComplete() {
|
||||
for (int i = 0; i < mBatchCompleteListenerModules.size(); i++) {
|
||||
mBatchCompleteListenerModules.get(i).onBatchComplete();
|
||||
}
|
||||
}
|
||||
|
||||
public void onExecutorUnregistered(ExecutorToken executorToken) {
|
||||
for (int i = 0; i < mOnExecutorUnregisteredListenerModules.size(); i++) {
|
||||
mOnExecutorUnregisteredListenerModules.get(i).onExecutorDestroyed(executorToken);
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends NativeModule> boolean hasModule(Class<T> moduleInterface) {
|
||||
return mModuleInstances.containsKey(moduleInterface);
|
||||
}
|
||||
|
||||
public <T extends NativeModule> T getModule(Class<T> moduleInterface) {
|
||||
return (T) Assertions.assertNotNull(mModuleInstances.get(moduleInterface));
|
||||
}
|
||||
|
||||
public Collection<NativeModule> getAllModules() {
|
||||
return mModuleInstances.values();
|
||||
}
|
||||
|
||||
private static class ModuleDefinition {
|
||||
public final int id;
|
||||
public final String name;
|
||||
public final NativeModule target;
|
||||
public final ArrayList<MethodRegistration> methods;
|
||||
|
||||
public ModuleDefinition(int id, String name, NativeModule target) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.target = target;
|
||||
this.methods = new ArrayList<MethodRegistration>();
|
||||
|
||||
for (Map.Entry<String, NativeModule.NativeMethod> entry : target.getMethods().entrySet()) {
|
||||
this.methods.add(
|
||||
new MethodRegistration(
|
||||
entry.getKey(), "NativeCall__" + target.getName() + "_" + entry.getKey(),
|
||||
entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
public void call(
|
||||
CatalystInstance catalystInstance,
|
||||
ExecutorToken executorToken,
|
||||
int methodId,
|
||||
ReadableNativeArray parameters) {
|
||||
MethodRegistration method = this.methods.get(methodId);
|
||||
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, method.tracingName);
|
||||
try {
|
||||
this.methods.get(methodId).method.invoke(catalystInstance, executorToken, parameters);
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class MethodRegistration {
|
||||
public MethodRegistration(String name, String tracingName, NativeModule.NativeMethod method) {
|
||||
this.name = name;
|
||||
this.tracingName = tracingName;
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public String name;
|
||||
public String tracingName;
|
||||
public NativeModule.NativeMethod method;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private final HashMap<String, NativeModule> mModules = MapBuilder.newHashMap();
|
||||
|
||||
public Builder add(NativeModule module) {
|
||||
NativeModule existing = mModules.get(module.getName());
|
||||
if (existing != null && !module.canOverrideExistingModule()) {
|
||||
throw new IllegalStateException("Native module " + module.getClass().getSimpleName() +
|
||||
" tried to override " + existing.getClass().getSimpleName() + " for module name " +
|
||||
module.getName() + ". If this was your intention, return true from " +
|
||||
module.getClass().getSimpleName() + "#canOverrideExistingModule()");
|
||||
}
|
||||
mModules.put(module.getName(), module);
|
||||
return this;
|
||||
}
|
||||
|
||||
public NativeModuleRegistry build() {
|
||||
List<ModuleDefinition> moduleTable = new ArrayList<>();
|
||||
Map<Class<? extends NativeModule>, NativeModule> moduleInstances = new HashMap<>();
|
||||
|
||||
int idx = 0;
|
||||
for (NativeModule module : mModules.values()) {
|
||||
ModuleDefinition moduleDef = new ModuleDefinition(idx++, module.getName(), module);
|
||||
moduleTable.add(moduleDef);
|
||||
moduleInstances.put(module.getClass(), module);
|
||||
}
|
||||
return new NativeModuleRegistry(moduleTable, moduleInstances);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.bridge;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.facebook.soloader.SoLoader;
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
|
||||
/**
|
||||
* JavaScript executor that delegates JS calls processed by native code back to a java version
|
||||
* of the native executor interface.
|
||||
*
|
||||
* When set as a executor with {@link CatalystInstance.Builder}, catalyst native code will delegate
|
||||
* low level javascript calls to the implementation of {@link JavaJSExecutor} interface provided
|
||||
* with the constructor of this class.
|
||||
*/
|
||||
@DoNotStrip
|
||||
public class ProxyJavaScriptExecutor extends JavaScriptExecutor {
|
||||
static {
|
||||
ReactBridge.staticInit();
|
||||
}
|
||||
|
||||
public static class Factory implements JavaScriptExecutor.Factory {
|
||||
private final JavaJSExecutor.Factory mJavaJSExecutorFactory;
|
||||
|
||||
public Factory(JavaJSExecutor.Factory javaJSExecutorFactory) {
|
||||
mJavaJSExecutorFactory = javaJSExecutorFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaScriptExecutor create(WritableNativeMap jscConfig) throws Exception {
|
||||
return new ProxyJavaScriptExecutor(mJavaJSExecutorFactory.create());
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable JavaJSExecutor mJavaJSExecutor;
|
||||
|
||||
/**
|
||||
* Create {@link ProxyJavaScriptExecutor} instance
|
||||
* @param executor implementation of {@link JavaJSExecutor} which will be responsible for handling
|
||||
* javascript calls
|
||||
*/
|
||||
public ProxyJavaScriptExecutor(JavaJSExecutor executor) {
|
||||
mJavaJSExecutor = executor;
|
||||
initialize(executor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (mJavaJSExecutor != null) {
|
||||
mJavaJSExecutor.close();
|
||||
mJavaJSExecutor = null;
|
||||
}
|
||||
}
|
||||
|
||||
private native void initialize(JavaJSExecutor executor);
|
||||
|
||||
}
|
||||
@@ -9,20 +9,9 @@
|
||||
|
||||
package com.facebook.react.bridge;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
|
||||
import com.facebook.react.bridge.queue.MessageQueueThread;
|
||||
import com.facebook.jni.Countable;
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
/**
|
||||
* Interface to the JS execution environment and means of transport for messages Java<->JS.
|
||||
*/
|
||||
@DoNotStrip
|
||||
public class ReactBridge extends Countable {
|
||||
public class ReactBridge {
|
||||
|
||||
private static final String REACT_NATIVE_LIB = "reactnativejni";
|
||||
private static final String XREACT_NATIVE_LIB = "reactnativejnifb";
|
||||
@@ -31,81 +20,8 @@ public class ReactBridge extends Countable {
|
||||
staticInit();
|
||||
}
|
||||
|
||||
private final ReactCallback mCallback;
|
||||
private final JavaScriptExecutor mJSExecutor;
|
||||
private final MessageQueueThread mNativeModulesQueueThread;
|
||||
|
||||
public static void staticInit() {
|
||||
SoLoader.loadLibrary(REACT_NATIVE_LIB);
|
||||
SoLoader.loadLibrary(XREACT_NATIVE_LIB);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param jsExecutor the JS executor to use to run JS
|
||||
* @param callback the callback class used to invoke native modules
|
||||
* @param nativeModulesQueueThread the MessageQueueThread the callbacks should be invoked on
|
||||
*/
|
||||
public ReactBridge(
|
||||
JavaScriptExecutor jsExecutor,
|
||||
ReactCallback callback,
|
||||
MessageQueueThread nativeModulesQueueThread) {
|
||||
mJSExecutor = jsExecutor;
|
||||
mCallback = callback;
|
||||
mNativeModulesQueueThread = nativeModulesQueueThread;
|
||||
initialize(jsExecutor, callback, mNativeModulesQueueThread);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
mJSExecutor.close();
|
||||
mJSExecutor.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
public void handleMemoryPressure(MemoryPressure level) {
|
||||
switch (level) {
|
||||
case UI_HIDDEN:
|
||||
handleMemoryPressureUiHidden();
|
||||
break;
|
||||
case MODERATE:
|
||||
handleMemoryPressureModerate();
|
||||
break;
|
||||
case CRITICAL:
|
||||
handleMemoryPressureCritical();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown level: " + level);
|
||||
}
|
||||
}
|
||||
|
||||
private native void initialize(
|
||||
JavaScriptExecutor jsExecutor,
|
||||
ReactCallback callback,
|
||||
MessageQueueThread nativeModulesQueueThread);
|
||||
|
||||
/**
|
||||
* All native functions are not thread safe and appropriate queues should be used
|
||||
*/
|
||||
public native void loadScriptFromAssets(AssetManager assetManager, String assetName);
|
||||
public native void loadScriptFromFile(@Nullable String fileName, @Nullable String sourceURL);
|
||||
public native void callFunction(ExecutorToken executorToken, String module, String method, NativeArray arguments);
|
||||
public native void invokeCallback(ExecutorToken executorToken, int callbackID, NativeArray arguments);
|
||||
public native void setGlobalVariable(String propertyName, String jsonEncodedArgument);
|
||||
public native boolean supportsProfiling();
|
||||
public native void startProfiler(String title);
|
||||
public native void stopProfiler(String title, String filename);
|
||||
public native ExecutorToken getMainExecutorToken();
|
||||
private native void handleMemoryPressureUiHidden();
|
||||
private native void handleMemoryPressureModerate();
|
||||
private native void handleMemoryPressureCritical();
|
||||
public native void destroy();
|
||||
|
||||
/**
|
||||
* This method will return a long representing the underlying JSGlobalContextRef pointer or
|
||||
* 0 (representing NULL) when in Chrome debug mode, and is only useful if passed back through
|
||||
* the JNI to native code that will use it with the JavaScriptCore C API.
|
||||
* **WARNING:** This method is *experimental* and should only be used when no other option is
|
||||
* available. It will likely change in a future release!
|
||||
*/
|
||||
public native long getJavaScriptContextNativePtrExperimental();
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.bridge;
|
||||
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
|
||||
@DoNotStrip
|
||||
public interface ReactCallback {
|
||||
|
||||
@DoNotStrip
|
||||
void call(ExecutorToken executorToken, int moduleId, int methodId, ReadableNativeArray parameters);
|
||||
|
||||
@DoNotStrip
|
||||
void onBatchComplete();
|
||||
|
||||
@DoNotStrip
|
||||
void onExecutorUnregistered(ExecutorToken executorToken);
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.bridge.webworkers;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
import com.facebook.react.bridge.queue.MessageQueueThread;
|
||||
import com.facebook.react.bridge.queue.MessageQueueThreadImpl;
|
||||
import com.facebook.react.bridge.queue.ProxyQueueThreadExceptionHandler;
|
||||
import com.facebook.react.common.build.ReactBuildConfig;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okio.Okio;
|
||||
import okio.Sink;
|
||||
|
||||
@DoNotStrip
|
||||
public class WebWorkers {
|
||||
|
||||
/**
|
||||
* Creates a new MessageQueueThread for a background web worker owned by the JS thread with the
|
||||
* given MessageQueueThread.
|
||||
*/
|
||||
@DoNotStrip
|
||||
public static MessageQueueThread createWebWorkerThread(int id, MessageQueueThread ownerThread) {
|
||||
return MessageQueueThreadImpl.startNewBackgroundThread(
|
||||
"web-worker-" + id,
|
||||
new ProxyQueueThreadExceptionHandler(ownerThread));
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method used to help develop web workers on debug builds. In release builds, worker
|
||||
* scripts need to be packaged with the app, but in dev mode we want to fetch/reload the worker
|
||||
* script on the fly from the packager. This method fetches the given URL *synchronously* and
|
||||
* writes it to the specified temp file.
|
||||
*
|
||||
* This is exposed from Java only because we don't want to add a C++ networking library dependency
|
||||
*
|
||||
* NB: The caller is responsible for deleting the file specified by outFileName when they're done
|
||||
* with it.
|
||||
* NB: We write to a temp file instead of returning a String because, depending on the size of the
|
||||
* worker script, allocating the full script string on the Java heap can cause an OOM.
|
||||
*/
|
||||
public static void downloadScriptToFileSync(String url, String outFileName) {
|
||||
if (!ReactBuildConfig.DEBUG) {
|
||||
throw new RuntimeException(
|
||||
"For security reasons, downloading scripts is only allowed in debug builds.");
|
||||
}
|
||||
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
final File out = new File(outFileName);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.build();
|
||||
|
||||
try {
|
||||
Response response = client.newCall(request).execute();
|
||||
|
||||
Sink output = Okio.sink(out);
|
||||
Okio.buffer(response.body().source()).readAll(output);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Exception downloading web worker script to file", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user