Move new bridge java stuff to OSS
Reviewed By: mhorowitz Differential Revision: D3300152 fbshipit-source-id: 9a76b10579bbfc5bde3a5094b99b64c38f4c1da9
This commit is contained in:
parent
5c6bbf7f1f
commit
3b3b46d86e
|
@ -1,22 +1,41 @@
|
||||||
include_defs('//ReactAndroid/DEFS')
|
include_defs('//ReactAndroid/DEFS')
|
||||||
|
|
||||||
|
XREACT_SRCS = [
|
||||||
|
'XReactInstanceManager.java',
|
||||||
|
'XReactInstanceManagerImpl.java',
|
||||||
|
]
|
||||||
|
|
||||||
|
DEPS = [
|
||||||
|
react_native_target('java/com/facebook/react/bridge:bridge'),
|
||||||
|
react_native_target('java/com/facebook/react/common:common'),
|
||||||
|
react_native_target('java/com/facebook/react/devsupport:devsupport'),
|
||||||
|
react_native_target('java/com/facebook/react/modules/core:core'),
|
||||||
|
react_native_target('java/com/facebook/react/modules/debug:debug'),
|
||||||
|
react_native_target('java/com/facebook/react/modules/systeminfo:systeminfo'),
|
||||||
|
react_native_target('java/com/facebook/react/modules/toast:toast'),
|
||||||
|
react_native_target('java/com/facebook/react/uimanager:uimanager'),
|
||||||
|
react_native_dep('java/com/facebook/systrace:systrace'),
|
||||||
|
react_native_dep('libraries/fbcore/src/main/java/com/facebook/common/logging:logging'),
|
||||||
|
react_native_dep('libraries/soloader/java/com/facebook/soloader:soloader'),
|
||||||
|
react_native_dep('third-party/java/infer-annotations:infer-annotations'),
|
||||||
|
react_native_dep('third-party/java/jsr-305:jsr-305'),
|
||||||
|
]
|
||||||
|
|
||||||
android_library(
|
android_library(
|
||||||
name = 'react',
|
name = 'react',
|
||||||
srcs = glob(['*.java']),
|
srcs = glob(['*.java'], excludes=XREACT_SRCS),
|
||||||
deps = [
|
deps = DEPS,
|
||||||
react_native_target('java/com/facebook/react/bridge:bridge'),
|
visibility = [
|
||||||
react_native_target('java/com/facebook/react/common:common'),
|
'PUBLIC',
|
||||||
react_native_target('java/com/facebook/react/devsupport:devsupport'),
|
],
|
||||||
react_native_target('java/com/facebook/react/modules/core:core'),
|
)
|
||||||
react_native_target('java/com/facebook/react/modules/debug:debug'),
|
|
||||||
react_native_target('java/com/facebook/react/modules/systeminfo:systeminfo'),
|
android_library(
|
||||||
react_native_target('java/com/facebook/react/modules/toast:toast'),
|
name = 'xreact',
|
||||||
react_native_target('java/com/facebook/react/uimanager:uimanager'),
|
srcs = XREACT_SRCS,
|
||||||
react_native_dep('java/com/facebook/systrace:systrace'),
|
deps = DEPS + [
|
||||||
react_native_dep('libraries/fbcore/src/main/java/com/facebook/common/logging:logging'),
|
':react',
|
||||||
react_native_dep('libraries/soloader/java/com/facebook/soloader:soloader'),
|
react_native_target('java/com/facebook/react/cxxbridge:bridge'),
|
||||||
react_native_dep('third-party/java/infer-annotations:infer-annotations'),
|
|
||||||
react_native_dep('third-party/java/jsr-305:jsr-305'),
|
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
'PUBLIC',
|
'PUBLIC',
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
/**
|
||||||
|
* 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 com.facebook.infer.annotation.Assertions;
|
||||||
|
import com.facebook.react.uimanager.UIImplementationProvider;
|
||||||
|
|
||||||
|
public abstract class XReactInstanceManager {
|
||||||
|
/**
|
||||||
|
* Creates a builder that is capable of creating an instance of {@link XReactInstanceManagerImpl}.
|
||||||
|
*/
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder class for {@link XReactInstanceManagerImpl}
|
||||||
|
*/
|
||||||
|
public static class Builder extends ReactInstanceManager.Builder {
|
||||||
|
/**
|
||||||
|
* Instantiates a new {@link ReactInstanceManagerImpl}.
|
||||||
|
* Before calling {@code build}, the following must be called:
|
||||||
|
* <ul>
|
||||||
|
* <li> {@link #setApplication}
|
||||||
|
* <li> {@link #setCurrentActivity} if the activity has already resumed
|
||||||
|
* <li> {@link #setDefaultHardwareBackBtnHandler} if the activity has already resumed
|
||||||
|
* <li> {@link #setJSBundleFile} or {@link #setJSMainModuleName}
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public ReactInstanceManager build() {
|
||||||
|
Assertions.assertCondition(
|
||||||
|
mUseDeveloperSupport || mJSBundleFile != null,
|
||||||
|
"JS Bundle File has to be provided when dev support is disabled");
|
||||||
|
|
||||||
|
Assertions.assertCondition(
|
||||||
|
mJSMainModuleName != null || mJSBundleFile != null,
|
||||||
|
"Either MainModuleName or JS Bundle File needs to be provided");
|
||||||
|
|
||||||
|
if (mUIImplementationProvider == null) {
|
||||||
|
// create default UIImplementationProvider if the provided one is null.
|
||||||
|
mUIImplementationProvider = new UIImplementationProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new XReactInstanceManagerImpl(
|
||||||
|
Assertions.assertNotNull(
|
||||||
|
mApplication,
|
||||||
|
"Application property has not been set with this builder"),
|
||||||
|
mCurrentActivity,
|
||||||
|
mDefaultHardwareBackBtnHandler,
|
||||||
|
mJSBundleFile,
|
||||||
|
mJSMainModuleName,
|
||||||
|
mPackages,
|
||||||
|
mUseDeveloperSupport,
|
||||||
|
mBridgeIdleDebugListener,
|
||||||
|
Assertions.assertNotNull(mInitialLifecycleState, "Initial lifecycle state was not set"),
|
||||||
|
mUIImplementationProvider,
|
||||||
|
mNativeModuleCallExceptionHandler,
|
||||||
|
mJSCConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,865 @@
|
||||||
|
/**
|
||||||
|
* 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 com.facebook.common.logging.FLog;
|
||||||
|
import com.facebook.infer.annotation.Assertions;
|
||||||
|
import com.facebook.react.bridge.CatalystInstance;
|
||||||
|
import com.facebook.react.bridge.JavaJSExecutor;
|
||||||
|
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.NotThreadSafeBridgeIdleDebugListener;
|
||||||
|
import com.facebook.react.bridge.ReactApplicationContext;
|
||||||
|
import com.facebook.react.bridge.ReactContext;
|
||||||
|
import com.facebook.react.bridge.ReactMarker;
|
||||||
|
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.cxxbridge.Arguments;
|
||||||
|
import com.facebook.react.cxxbridge.CatalystInstanceImpl;
|
||||||
|
import com.facebook.react.cxxbridge.JSBundleLoader;
|
||||||
|
import com.facebook.react.cxxbridge.JSCJavaScriptExecutor;
|
||||||
|
import com.facebook.react.cxxbridge.JavaScriptExecutor;
|
||||||
|
import com.facebook.react.cxxbridge.NativeModuleRegistry;
|
||||||
|
import com.facebook.react.cxxbridge.ProxyJavaScriptExecutor;
|
||||||
|
import com.facebook.react.cxxbridge.UiThreadUtil;
|
||||||
|
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.modules.core.DefaultHardwareBackBtnHandler;
|
||||||
|
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||||
|
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_JS_MODULE_CONFIG_END;
|
||||||
|
import static com.facebook.react.bridge.ReactMarkerConstants.BUILD_JS_MODULE_CONFIG_START;
|
||||||
|
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_END;
|
||||||
|
import static com.facebook.react.bridge.ReactMarkerConstants.RUN_JS_BUNDLE_START;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is managing instances of {@link CatalystInstance}. It expose a way to configure
|
||||||
|
* catalyst instance 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 XReactInstanceManagerImpl} 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 XReactInstanceManagerImpl 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 final ReactInstanceDevCommandsHandler mDevInterface =
|
||||||
|
new ReactInstanceDevCommandsHandler() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReloadWithJSDebugger(JavaJSExecutor.Factory jsExecutorFactory) {
|
||||||
|
XReactInstanceManagerImpl.this.onReloadWithJSDebugger(jsExecutorFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onJSBundleLoadedFromServer() {
|
||||||
|
XReactInstanceManagerImpl.this.onJSBundleLoadedFromServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void toggleElementInspector() {
|
||||||
|
XReactInstanceManagerImpl.this.toggleElementInspector();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final DefaultHardwareBackBtnHandler mBackBtnHandler =
|
||||||
|
new DefaultHardwareBackBtnHandler() {
|
||||||
|
@Override
|
||||||
|
public void invokeDefaultOnBackPressed() {
|
||||||
|
XReactInstanceManagerImpl.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();
|
||||||
|
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 */ XReactInstanceManagerImpl(
|
||||||
|
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) {
|
||||||
|
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;
|
||||||
|
mDevSupportManager = DevSupportManagerFactory.create(
|
||||||
|
applicationContext,
|
||||||
|
mDevInterface,
|
||||||
|
mJSMainModuleName,
|
||||||
|
useDeveloperSupport);
|
||||||
|
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 XReactInstanceManagerImpl
|
||||||
|
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(
|
||||||
|
mJSCConfig == null ? new WritableNativeMap() : mJSCConfig.getConfigMap()),
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
* XReactInstanceManagerImpl}, 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 XReactInstanceManagerImpl}.
|
||||||
|
*/
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LifecycleState getLifecycleState() {
|
||||||
|
return mLifecycleState;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onReloadWithJSDebugger(JavaJSExecutor.Factory jsExecutorFactory) {
|
||||||
|
recreateReactContextInBackground(
|
||||||
|
new ProxyJavaScriptExecutor.Factory(jsExecutorFactory),
|
||||||
|
JSBundleLoader.createRemoteDebuggerBundleLoader(
|
||||||
|
mDevSupportManager.getJSBundleURLForRemoteDebugging(),
|
||||||
|
mDevSupportManager.getSourceUrl()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onJSBundleLoadedFromServer() {
|
||||||
|
recreateReactContextInBackground(
|
||||||
|
new JSCJavaScriptExecutor.Factory(
|
||||||
|
mJSCConfig == null ? new WritableNativeMap() : mJSCConfig.getConfigMap()),
|
||||||
|
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);
|
||||||
|
@Nullable Bundle launchOptions = rootView.getLaunchOptions();
|
||||||
|
WritableMap initialProps = Arguments.makeNativeMap(launchOptions);
|
||||||
|
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);
|
||||||
|
mSourceUrl = jsBundleLoader.getSourceUrl();
|
||||||
|
NativeModuleRegistry.Builder nativeRegistryBuilder = new NativeModuleRegistry.Builder();
|
||||||
|
JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder();
|
||||||
|
|
||||||
|
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);
|
||||||
|
// CREATE_CATALYST_INSTANCE_END is in JSCExecutor.cpp
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
reactContext.initializeWithInstance(catalystInstance);
|
||||||
|
|
||||||
|
ReactMarker.logMarker(RUN_JS_BUNDLE_START);
|
||||||
|
catalystInstance.getReactQueueConfiguration().getJSQueueThread().runOnQueue(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "runJSBundle");
|
||||||
|
try {
|
||||||
|
catalystInstance.runJSBundle();
|
||||||
|
} finally {
|
||||||
|
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||||
|
ReactMarker.logMarker(RUN_JS_BUNDLE_END);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,159 @@
|
||||||
|
/**
|
||||||
|
* 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.cxxbridge;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
|
||||||
|
import java.util.AbstractList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.ReadableMap;
|
||||||
|
import com.facebook.react.bridge.ReadableMapKeySetIterator;
|
||||||
|
import com.facebook.react.bridge.ReadableType;
|
||||||
|
import com.facebook.react.bridge.WritableNativeArray;
|
||||||
|
import com.facebook.react.bridge.WritableNativeMap;
|
||||||
|
|
||||||
|
public class Arguments {
|
||||||
|
private static Object makeNativeObject(Object object) {
|
||||||
|
if (object == null) {
|
||||||
|
return null;
|
||||||
|
} else if (object instanceof Float ||
|
||||||
|
object instanceof Long ||
|
||||||
|
object instanceof Byte ||
|
||||||
|
object instanceof Short) {
|
||||||
|
return new Double(((Number) object).doubleValue());
|
||||||
|
} else if (object.getClass().isArray()) {
|
||||||
|
return makeNativeArray(object);
|
||||||
|
} else if (object instanceof List) {
|
||||||
|
return makeNativeArray((List) object);
|
||||||
|
} else if (object instanceof Map) {
|
||||||
|
return makeNativeMap((Map<String, Object>) object);
|
||||||
|
} else if (object instanceof Bundle) {
|
||||||
|
return makeNativeMap((Bundle) object);
|
||||||
|
} else {
|
||||||
|
// Boolean, Integer, Double, String, WritableNativeArray, WritableNativeMap
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method converts a List into a NativeArray. The data types supported
|
||||||
|
* are boolean, int, float, double, and String. List, Map, and Bundle
|
||||||
|
* objects, as well as arrays, containing values of the above types and/or
|
||||||
|
* null, or any recursive arrangement of these, are also supported. The best
|
||||||
|
* way to think of this is a way to generate a Java representation of a json
|
||||||
|
* list, from Java types which have a natural representation in json.
|
||||||
|
*/
|
||||||
|
public static WritableNativeArray makeNativeArray(List objects) {
|
||||||
|
WritableNativeArray nativeArray = new WritableNativeArray();
|
||||||
|
if (objects == null) {
|
||||||
|
return nativeArray;
|
||||||
|
}
|
||||||
|
for (Object elem : objects) {
|
||||||
|
elem = makeNativeObject(elem);
|
||||||
|
if (elem == null) {
|
||||||
|
nativeArray.pushNull();
|
||||||
|
} else if (elem instanceof Boolean) {
|
||||||
|
nativeArray.pushBoolean((Boolean) elem);
|
||||||
|
} else if (elem instanceof Integer) {
|
||||||
|
nativeArray.pushInt((Integer) elem);
|
||||||
|
} else if (elem instanceof Double) {
|
||||||
|
nativeArray.pushDouble((Double) elem);
|
||||||
|
} else if (elem instanceof String) {
|
||||||
|
nativeArray.pushString((String) elem);
|
||||||
|
} else if (elem instanceof WritableNativeArray) {
|
||||||
|
nativeArray.pushArray((WritableNativeArray) elem);
|
||||||
|
} else if (elem instanceof WritableNativeMap) {
|
||||||
|
nativeArray.pushMap((WritableNativeMap) elem);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Could not convert " + elem.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nativeArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This overload is like the above, but uses reflection to operate on any
|
||||||
|
* primitive or object type.
|
||||||
|
*/
|
||||||
|
public static <T> WritableNativeArray makeNativeArray(final Object objects) {
|
||||||
|
if (objects == null) {
|
||||||
|
return new WritableNativeArray();
|
||||||
|
}
|
||||||
|
// No explicit check for objects's type here. If it's not an array, the
|
||||||
|
// Array methods will throw IllegalArgumentException.
|
||||||
|
return makeNativeArray(new AbstractList() {
|
||||||
|
public int size() {
|
||||||
|
return Array.getLength(objects);
|
||||||
|
}
|
||||||
|
public Object get(int index) {
|
||||||
|
return Array.get(objects, index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addEntry(WritableNativeMap nativeMap, String key, Object value) {
|
||||||
|
value = makeNativeObject(value);
|
||||||
|
if (value == null) {
|
||||||
|
nativeMap.putNull(key);
|
||||||
|
} else if (value instanceof Boolean) {
|
||||||
|
nativeMap.putBoolean(key, (Boolean) value);
|
||||||
|
} else if (value instanceof Integer) {
|
||||||
|
nativeMap.putInt(key, (Integer) value);
|
||||||
|
} else if (value instanceof Number) {
|
||||||
|
nativeMap.putDouble(key, ((Number) value).doubleValue());
|
||||||
|
} else if (value instanceof String) {
|
||||||
|
nativeMap.putString(key, (String) value);
|
||||||
|
} else if (value instanceof WritableNativeArray) {
|
||||||
|
nativeMap.putArray(key, (WritableNativeArray) value);
|
||||||
|
} else if (value instanceof WritableNativeMap) {
|
||||||
|
nativeMap.putMap(key, (WritableNativeMap) value);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Could not convert " + value.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method converts a Map into a NativeMap. Value types are supported as
|
||||||
|
* with makeNativeArray. The best way to think of this is a way to generate
|
||||||
|
* a Java representation of a json object, from Java types which have a
|
||||||
|
* natural representation in json.
|
||||||
|
*/
|
||||||
|
public static WritableNativeMap makeNativeMap(Map<String, Object> objects) {
|
||||||
|
WritableNativeMap nativeMap = new WritableNativeMap();
|
||||||
|
if (objects == null) {
|
||||||
|
return nativeMap;
|
||||||
|
}
|
||||||
|
for (Map.Entry<String, Object> entry : objects.entrySet()) {
|
||||||
|
addEntry(nativeMap, entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
return nativeMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like the above, but takes a Bundle instead of a Map.
|
||||||
|
*/
|
||||||
|
public static WritableNativeMap makeNativeMap(Bundle bundle) {
|
||||||
|
WritableNativeMap nativeMap = new WritableNativeMap();
|
||||||
|
if (bundle == null) {
|
||||||
|
return nativeMap;
|
||||||
|
}
|
||||||
|
for (String key: bundle.keySet()) {
|
||||||
|
addEntry(nativeMap, key, bundle.get(key));
|
||||||
|
}
|
||||||
|
return nativeMap;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
include_defs('//ReactAndroid/DEFS')
|
||||||
|
|
||||||
|
android_library(
|
||||||
|
name = 'bridge',
|
||||||
|
srcs = glob(['**/*.java']),
|
||||||
|
exported_deps = [
|
||||||
|
react_native_dep('java/com/facebook/jni:jni'),
|
||||||
|
react_native_dep('java/com/facebook/proguard/annotations:annotations'),
|
||||||
|
],
|
||||||
|
proguard_config = 'bridge.pro',
|
||||||
|
deps = [
|
||||||
|
'//libraries/fbcore/src/main/java/com/facebook/common/logging:logging',
|
||||||
|
react_native_dep('java/com/facebook/systrace:systrace'),
|
||||||
|
react_native_dep('libraries/soloader/java/com/facebook/soloader:soloader'),
|
||||||
|
# TODO mhorowitz:
|
||||||
|
# java/com/facebook/catalyst/js/react-native-github/ReactAndroid/src/main/java/com/facebook/react/bridge/
|
||||||
|
# lacks a similar dependency to this. This means that the
|
||||||
|
# loadLibrary calls in it are not guaranteed to succeed. This is
|
||||||
|
# kind of a mess for the jni/jni-internal stuff. In theory, we
|
||||||
|
# should be creating -internal android_library rules, too. In
|
||||||
|
# practice, since these are resolved at runtime, putting the
|
||||||
|
# dependency in the app works, too. gross.
|
||||||
|
# '//native/react/jni:jni-internal',
|
||||||
|
react_native_dep('third-party/java/infer-annotations:infer-annotations'),
|
||||||
|
react_native_dep('third-party/java/jackson:core'),
|
||||||
|
react_native_dep('third-party/java/jsr-305:jsr-305'),
|
||||||
|
react_native_dep('third-party/java/okhttp:okhttp3-ws'),
|
||||||
|
react_native_target('java/com/facebook/react/bridge:bridge'),
|
||||||
|
react_native_target('java/com/facebook/react/common:common'),
|
||||||
|
],
|
||||||
|
visibility = [
|
||||||
|
'PUBLIC',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
project_config(
|
||||||
|
src_target = ':bridge',
|
||||||
|
)
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
|
package com.facebook.react.cxxbridge;
|
||||||
|
|
||||||
|
import com.facebook.jni.HybridData;
|
||||||
|
import com.facebook.proguard.annotations.DoNotStrip;
|
||||||
|
import com.facebook.react.bridge.*;
|
||||||
|
|
||||||
|
import static com.facebook.react.bridge.Arguments.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback impl that calls directly into the cxxbridge. Created from C++.
|
||||||
|
*/
|
||||||
|
@DoNotStrip
|
||||||
|
public class CallbackImpl implements Callback {
|
||||||
|
@DoNotStrip
|
||||||
|
private final HybridData mHybridData;
|
||||||
|
|
||||||
|
@DoNotStrip
|
||||||
|
private CallbackImpl(HybridData hybridData) {
|
||||||
|
mHybridData = hybridData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invoke(Object... args) {
|
||||||
|
nativeInvoke(fromJavaArgs(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
private native void nativeInvoke(NativeArray arguments);
|
||||||
|
}
|
|
@ -0,0 +1,461 @@
|
||||||
|
/**
|
||||||
|
* 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.cxxbridge;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import android.content.res.AssetManager;
|
||||||
|
|
||||||
|
import com.facebook.common.logging.FLog;
|
||||||
|
import com.facebook.jni.HybridData;
|
||||||
|
import com.facebook.react.bridge.CatalystInstance;
|
||||||
|
import com.facebook.react.bridge.ExecutorToken;
|
||||||
|
import com.facebook.react.bridge.JavaScriptModule;
|
||||||
|
import com.facebook.react.bridge.JavaScriptModuleRegistry;
|
||||||
|
import com.facebook.react.bridge.MemoryPressure;
|
||||||
|
import com.facebook.react.bridge.NativeArray;
|
||||||
|
import com.facebook.react.bridge.NativeModule;
|
||||||
|
import com.facebook.react.bridge.NativeModuleCallExceptionHandler;
|
||||||
|
import com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener;
|
||||||
|
import com.facebook.react.bridge.queue.ReactQueueConfiguration;
|
||||||
|
import com.facebook.react.bridge.queue.MessageQueueThread;
|
||||||
|
import com.facebook.react.bridge.queue.QueueThreadExceptionHandler;
|
||||||
|
import com.facebook.react.bridge.queue.ReactQueueConfigurationImpl;
|
||||||
|
import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec;
|
||||||
|
import com.facebook.proguard.annotations.DoNotStrip;
|
||||||
|
import com.facebook.react.common.ReactConstants;
|
||||||
|
import com.facebook.react.common.annotations.VisibleForTesting;
|
||||||
|
import com.facebook.infer.annotation.Assertions;
|
||||||
|
import com.facebook.soloader.SoLoader;
|
||||||
|
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 XReactInstanceManager which is in a different package.
|
||||||
|
*/
|
||||||
|
@DoNotStrip
|
||||||
|
public class CatalystInstanceImpl implements CatalystInstance {
|
||||||
|
|
||||||
|
/* package */ static final String REACT_NATIVE_LIB = "reactnativejnifb";
|
||||||
|
|
||||||
|
static {
|
||||||
|
SoLoader.loadLibrary(REACT_NATIVE_LIB);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int BRIDGE_SETUP_TIMEOUT_MS = 30000;
|
||||||
|
private static final int LOAD_JS_BUNDLE_TIMEOUT_MS = 30000;
|
||||||
|
|
||||||
|
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 mDestroyed = false;
|
||||||
|
private final TraceListener mTraceListener;
|
||||||
|
private final JavaScriptModuleRegistry mJSModuleRegistry;
|
||||||
|
private final JSBundleLoader mJSBundleLoader;
|
||||||
|
private ExecutorToken mMainExecutorToken;
|
||||||
|
|
||||||
|
private final NativeModuleRegistry mJavaRegistry;
|
||||||
|
private final NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler;
|
||||||
|
private boolean mInitialized = false;
|
||||||
|
|
||||||
|
private boolean mJSBundleHasLoaded;
|
||||||
|
|
||||||
|
// C++ parts
|
||||||
|
private final HybridData mHybridData;
|
||||||
|
private native static HybridData initHybrid();
|
||||||
|
|
||||||
|
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 Xplat Bridge.");
|
||||||
|
mHybridData = initHybrid();
|
||||||
|
|
||||||
|
mReactQueueConfiguration = ReactQueueConfigurationImpl.create(
|
||||||
|
ReactQueueConfigurationSpec,
|
||||||
|
new NativeExceptionHandler());
|
||||||
|
mBridgeIdleListeners = new CopyOnWriteArrayList<>();
|
||||||
|
mJavaRegistry = registry;
|
||||||
|
mJSModuleRegistry = jsModuleRegistry;
|
||||||
|
mJSBundleLoader = jsBundleLoader;
|
||||||
|
mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
|
||||||
|
mTraceListener = new JSProfilerTraceListener(this);
|
||||||
|
|
||||||
|
initializeBridge(
|
||||||
|
new BridgeCallback(this),
|
||||||
|
jsExecutor,
|
||||||
|
mReactQueueConfiguration.getJSQueueThread(),
|
||||||
|
mReactQueueConfiguration.getNativeModulesQueueThread(),
|
||||||
|
mJavaRegistry.getModuleRegistryHolder(this));
|
||||||
|
mMainExecutorToken = getMainExecutorToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BridgeCallback implements ReactCallback {
|
||||||
|
// We do this so the callback doesn't keep the CatalystInstanceImpl alive.
|
||||||
|
// In this case, the callback is held in C++ code, so the GC can't see it
|
||||||
|
// and determine there's an inaccessible cycle.
|
||||||
|
private final WeakReference<CatalystInstanceImpl> mOuter;
|
||||||
|
|
||||||
|
public BridgeCallback(CatalystInstanceImpl outer) {
|
||||||
|
mOuter = new WeakReference<CatalystInstanceImpl>(outer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBatchComplete() {
|
||||||
|
CatalystInstanceImpl impl = mOuter.get();
|
||||||
|
if (impl != null) {
|
||||||
|
impl.mJavaRegistry.onBatchComplete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void incrementPendingJSCalls() {
|
||||||
|
CatalystInstanceImpl impl = mOuter.get();
|
||||||
|
if (impl != null) {
|
||||||
|
impl.incrementPendingJSCalls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void decrementPendingJSCalls() {
|
||||||
|
CatalystInstanceImpl impl = mOuter.get();
|
||||||
|
if (impl != null) {
|
||||||
|
impl.decrementPendingJSCalls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNativeException(Exception e) {
|
||||||
|
CatalystInstanceImpl impl = mOuter.get();
|
||||||
|
if (impl != null) {
|
||||||
|
impl.onNativeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private native void initializeBridge(ReactCallback callback,
|
||||||
|
JavaScriptExecutor jsExecutor,
|
||||||
|
MessageQueueThread jsQueue,
|
||||||
|
MessageQueueThread moduleQueue,
|
||||||
|
ModuleRegistryHolder registryHolder);
|
||||||
|
|
||||||
|
/* package */ native void loadScriptFromAssets(AssetManager assetManager, String assetURL);
|
||||||
|
/* package */ native void loadScriptFromFile(String fileName, String sourceURL);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runJSBundle() {
|
||||||
|
Assertions.assertCondition(!mJSBundleHasLoaded, "JS bundle was already loaded!");
|
||||||
|
mJSBundleHasLoaded = true;
|
||||||
|
// incrementPendingJSCalls();
|
||||||
|
mJSBundleLoader.loadScript(CatalystInstanceImpl.this);
|
||||||
|
// This is registered after JS starts since it makes a JS call
|
||||||
|
Systrace.registerListener(mTraceListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private native void callJSFunction(
|
||||||
|
ExecutorToken token,
|
||||||
|
String module,
|
||||||
|
String method,
|
||||||
|
NativeArray arguments,
|
||||||
|
String tracingName);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void callFunction(
|
||||||
|
ExecutorToken executorToken,
|
||||||
|
final String module,
|
||||||
|
final String method,
|
||||||
|
final NativeArray arguments,
|
||||||
|
final String tracingName) {
|
||||||
|
if (mDestroyed) {
|
||||||
|
FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callJSFunction(executorToken, module, method, arguments, tracingName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private native void callJSCallback(ExecutorToken executorToken, int callbackID, NativeArray arguments);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invokeCallback(ExecutorToken executorToken, final int callbackID, final NativeArray arguments) {
|
||||||
|
if (mDestroyed) {
|
||||||
|
FLog.w(ReactConstants.TAG, "Invoking JS callback after bridge has been destroyed.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callJSCallback(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();
|
||||||
|
|
||||||
|
if (mDestroyed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: tell all APIs to shut down
|
||||||
|
mDestroyed = true;
|
||||||
|
mHybridData.resetNative();
|
||||||
|
mJavaRegistry.notifyCatalystInstanceDestroy();
|
||||||
|
boolean wasIdle = (mPendingJSCalls.getAndSet(0) == 0);
|
||||||
|
if (!wasIdle && !mBridgeIdleListeners.isEmpty()) {
|
||||||
|
for (NotThreadSafeBridgeIdleDebugListener listener : mBridgeIdleListeners) {
|
||||||
|
listener.onTransitionToBridgeIdle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a noop if the listener was not yet registered.
|
||||||
|
Systrace.unregisterListener(mTraceListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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(mMainExecutorToken, jsInterface);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends JavaScriptModule> T getJSModule(ExecutorToken executorToken, Class<T> jsInterface) {
|
||||||
|
return Assertions.assertNotNull(mJSModuleRegistry)
|
||||||
|
.getJavaScriptModule(this, executorToken, jsInterface);
|
||||||
|
}
|
||||||
|
|
||||||
|
private native ExecutorToken getMainExecutorToken();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends NativeModule> boolean hasNativeModule(Class<T> nativeModuleInterface) {
|
||||||
|
return mJavaRegistry.hasModule(nativeModuleInterface);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is only ever called with UIManagerModule or CurrentViewerModule.
|
||||||
|
@Override
|
||||||
|
public <T extends NativeModule> T getNativeModule(Class<T> nativeModuleInterface) {
|
||||||
|
return mJavaRegistry.getModule(nativeModuleInterface);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is only used by com.facebook.react.modules.common.ModuleDataCleaner
|
||||||
|
@Override
|
||||||
|
public Collection<NativeModule> getNativeModules() {
|
||||||
|
return mJavaRegistry.getAllModules();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMemoryPressure(MemoryPressure 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
|
||||||
|
* onBatchComplete 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 native void setGlobalVariable(String propName, String jsonValue);
|
||||||
|
|
||||||
|
// TODO mhorowitz: add mDestroyed checks to the next three methods
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public native boolean supportsProfiling();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public native void startProfiler(String title);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public native void stopProfiler(String title, String filename);
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onNativeException(Exception e) {
|
||||||
|
mNativeModuleCallExceptionHandler.handleException(e);
|
||||||
|
mReactQueueConfiguration.getUIQueueThread().runOnQueue(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
onNativeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class JSProfilerTraceListener implements TraceListener {
|
||||||
|
// We do this so the callback doesn't keep the CatalystInstanceImpl alive.
|
||||||
|
// In this case, Systrace will keep the registered listener around forever
|
||||||
|
// if the CatalystInstanceImpl is not explicitly destroyed. These instances
|
||||||
|
// can still leak, but they are at least small.
|
||||||
|
private final WeakReference<CatalystInstanceImpl> mOuter;
|
||||||
|
|
||||||
|
public JSProfilerTraceListener(CatalystInstanceImpl outer) {
|
||||||
|
mOuter = new WeakReference<CatalystInstanceImpl>(outer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTraceStarted() {
|
||||||
|
CatalystInstanceImpl impl = mOuter.get();
|
||||||
|
if (impl != null) {
|
||||||
|
impl.getJSModule(com.facebook.react.bridge.Systrace.class).setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTraceStopped() {
|
||||||
|
CatalystInstanceImpl impl = mOuter.get();
|
||||||
|
if (impl != null) {
|
||||||
|
impl.getJSModule(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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
|
package com.facebook.react.cxxbridge;
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.BaseJavaModule;
|
||||||
|
import com.facebook.react.bridge.CatalystInstance;
|
||||||
|
import com.facebook.react.bridge.ExecutorToken;
|
||||||
|
import com.facebook.react.bridge.JsonWriter;
|
||||||
|
import com.facebook.react.bridge.NativeModule;
|
||||||
|
import com.facebook.react.bridge.ReactBridge;
|
||||||
|
import com.facebook.react.bridge.ReadableNativeArray;
|
||||||
|
|
||||||
|
import com.facebook.jni.HybridData;
|
||||||
|
import com.facebook.proguard.annotations.DoNotStrip;
|
||||||
|
import com.facebook.soloader.SoLoader;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Java Object which represents a cross-platform C++ module
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@DoNotStrip
|
||||||
|
public class CxxModuleWrapper implements NativeModule
|
||||||
|
{
|
||||||
|
static {
|
||||||
|
SoLoader.loadLibrary(CatalystInstanceImpl.REACT_NATIVE_LIB);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DoNotStrip
|
||||||
|
private HybridData mHybridData;
|
||||||
|
|
||||||
|
@DoNotStrip
|
||||||
|
private static class MethodWrapper implements NativeMethod
|
||||||
|
{
|
||||||
|
@DoNotStrip
|
||||||
|
HybridData mHybridData;
|
||||||
|
|
||||||
|
MethodWrapper() {
|
||||||
|
mHybridData = initHybrid();
|
||||||
|
}
|
||||||
|
|
||||||
|
public native HybridData initHybrid();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public native void invoke(CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray args);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return BaseJavaModule.METHOD_TYPE_REMOTE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CxxModuleWrapper(String library, String factory) {
|
||||||
|
SoLoader.loadLibrary(library);
|
||||||
|
mHybridData =
|
||||||
|
initHybrid(SoLoader.unpackLibraryAndDependencies(library).getAbsolutePath(), factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public native String getName();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public native Map<String, NativeMethod> getMethods();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeConstantsField(JsonWriter writer, String fieldName) throws IOException {
|
||||||
|
String constants = getConstantsJson();
|
||||||
|
if (constants == null || constants.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.name(fieldName).rawValue(constants);
|
||||||
|
}
|
||||||
|
|
||||||
|
public native String getConstantsJson();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canOverrideExistingModule() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsWebWorkers() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReactBridgeInitialized(ReactBridge bridge) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCatalystInstanceDestroy() {
|
||||||
|
mHybridData.resetNative();
|
||||||
|
}
|
||||||
|
|
||||||
|
// For creating a wrapper from C++, or from a derived class.
|
||||||
|
protected CxxModuleWrapper(HybridData hd) {
|
||||||
|
mHybridData = hd;
|
||||||
|
}
|
||||||
|
|
||||||
|
private native HybridData initHybrid(String soPath, String factory);
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
/**
|
||||||
|
* 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.cxxbridge;
|
||||||
|
|
||||||
|
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(CatalystInstanceImpl instance) {
|
||||||
|
if (fileName.startsWith("assets://")) {
|
||||||
|
instance.loadScriptFromAssets(context.getAssets(), fileName);
|
||||||
|
} else {
|
||||||
|
instance.loadScriptFromFile(fileName, fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSourceUrl() {
|
||||||
|
return 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(CatalystInstanceImpl instance) {
|
||||||
|
instance.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.
|
||||||
|
*/
|
||||||
|
public static JSBundleLoader createRemoteDebuggerBundleLoader(
|
||||||
|
final String proxySourceURL,
|
||||||
|
final String realSourceURL) {
|
||||||
|
return new JSBundleLoader() {
|
||||||
|
@Override
|
||||||
|
public void loadScript(CatalystInstanceImpl instance) {
|
||||||
|
instance.loadScriptFromFile(null, proxySourceURL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSourceUrl() {
|
||||||
|
return realSourceURL;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void loadScript(CatalystInstanceImpl instance);
|
||||||
|
public abstract String getSourceUrl();
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/**
|
||||||
|
* 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.cxxbridge;
|
||||||
|
|
||||||
|
import com.facebook.jni.HybridData;
|
||||||
|
import com.facebook.proguard.annotations.DoNotStrip;
|
||||||
|
import com.facebook.react.bridge.ReadableNativeArray;
|
||||||
|
import com.facebook.react.bridge.WritableNativeArray;
|
||||||
|
import com.facebook.react.bridge.WritableNativeMap;
|
||||||
|
import com.facebook.soloader.SoLoader;
|
||||||
|
|
||||||
|
@DoNotStrip
|
||||||
|
public class JSCJavaScriptExecutor extends JavaScriptExecutor {
|
||||||
|
public static class Factory implements JavaScriptExecutor.Factory {
|
||||||
|
private ReadableNativeArray mJSCConfig;
|
||||||
|
|
||||||
|
public Factory(WritableNativeMap jscConfig) {
|
||||||
|
// TODO (t10707444): use NativeMap, which requires moving NativeMap out of OnLoad.
|
||||||
|
WritableNativeArray array = new WritableNativeArray();
|
||||||
|
array.pushMap(jscConfig);
|
||||||
|
mJSCConfig = array;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaScriptExecutor create() throws Exception {
|
||||||
|
return new JSCJavaScriptExecutor(mJSCConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
SoLoader.loadLibrary(CatalystInstanceImpl.REACT_NATIVE_LIB);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSCJavaScriptExecutor(ReadableNativeArray jscConfig) {
|
||||||
|
super(initHybrid(jscConfig));
|
||||||
|
}
|
||||||
|
|
||||||
|
private native static HybridData initHybrid(ReadableNativeArray jscConfig);
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
/**
|
||||||
|
* 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.cxxbridge;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.facebook.proguard.annotations.DoNotStrip;
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.BaseJavaModule;
|
||||||
|
import com.facebook.react.bridge.CatalystInstance;
|
||||||
|
import com.facebook.react.bridge.ExecutorToken;
|
||||||
|
import com.facebook.react.bridge.NativeArray;
|
||||||
|
import com.facebook.react.bridge.ReadableNativeArray;
|
||||||
|
import com.facebook.react.bridge.WritableNativeArray;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is part of the glue which wraps a java BaseJavaModule in a C++
|
||||||
|
* NativeModule. This could all be in C++, but it's android-specific
|
||||||
|
* initialization code, and writing it this way is easier to read and means
|
||||||
|
* fewer JNI calls.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@DoNotStrip
|
||||||
|
/* package */ class JavaModuleWrapper {
|
||||||
|
@DoNotStrip
|
||||||
|
public class MethodDescriptor {
|
||||||
|
@DoNotStrip
|
||||||
|
Method method;
|
||||||
|
@DoNotStrip
|
||||||
|
String signature;
|
||||||
|
@DoNotStrip
|
||||||
|
String name;
|
||||||
|
@DoNotStrip
|
||||||
|
String type;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final CatalystInstance mCatalystInstance;
|
||||||
|
private final BaseJavaModule mModule;
|
||||||
|
private final ArrayList<BaseJavaModule.JavaMethod> mMethods;
|
||||||
|
|
||||||
|
public JavaModuleWrapper(CatalystInstance catalystinstance, BaseJavaModule module) {
|
||||||
|
mCatalystInstance = catalystinstance;
|
||||||
|
mModule = module;
|
||||||
|
mMethods = new ArrayList<BaseJavaModule.JavaMethod>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DoNotStrip
|
||||||
|
public BaseJavaModule getModule() {
|
||||||
|
return mModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
@DoNotStrip
|
||||||
|
public String getName() {
|
||||||
|
return mModule.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DoNotStrip
|
||||||
|
public List<MethodDescriptor> getMethodDescriptors() {
|
||||||
|
ArrayList<MethodDescriptor> descs = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Map.Entry<String, BaseJavaModule.NativeMethod> entry :
|
||||||
|
mModule.getMethods().entrySet()) {
|
||||||
|
MethodDescriptor md = new MethodDescriptor();
|
||||||
|
md.name = entry.getKey();
|
||||||
|
md.type = entry.getValue().getType();
|
||||||
|
|
||||||
|
BaseJavaModule.JavaMethod method = (BaseJavaModule.JavaMethod) entry.getValue();
|
||||||
|
mMethods.add(method);
|
||||||
|
|
||||||
|
descs.add(md);
|
||||||
|
}
|
||||||
|
|
||||||
|
return descs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@DoNotStrip
|
||||||
|
public List<MethodDescriptor> newGetMethodDescriptors() {
|
||||||
|
ArrayList<MethodDescriptor> descs = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Map.Entry<String, BaseJavaModule.NativeMethod> entry :
|
||||||
|
mModule.getMethods().entrySet()) {
|
||||||
|
MethodDescriptor md = new MethodDescriptor();
|
||||||
|
md.name = entry.getKey();
|
||||||
|
md.type = entry.getValue().getType();
|
||||||
|
|
||||||
|
BaseJavaModule.JavaMethod method = (BaseJavaModule.JavaMethod) entry.getValue();
|
||||||
|
md.method = method.getMethod();
|
||||||
|
md.signature = method.getSignature();
|
||||||
|
|
||||||
|
descs.add(md);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, BaseJavaModule.SyncNativeHook> entry :
|
||||||
|
mModule.getSyncHooks().entrySet()) {
|
||||||
|
MethodDescriptor md = new MethodDescriptor();
|
||||||
|
md.name = entry.getKey();
|
||||||
|
md.type = BaseJavaModule.METHOD_TYPE_SYNC_HOOK;
|
||||||
|
|
||||||
|
BaseJavaModule.SyncJavaHook method = (BaseJavaModule.SyncJavaHook) entry.getValue();
|
||||||
|
md.method = method.getMethod();
|
||||||
|
md.signature = method.getSignature();
|
||||||
|
|
||||||
|
descs.add(md);
|
||||||
|
}
|
||||||
|
|
||||||
|
return descs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO mhorowitz: make this return NativeMap, which requires moving
|
||||||
|
// NativeMap out of OnLoad.
|
||||||
|
@DoNotStrip
|
||||||
|
public NativeArray getConstants() {
|
||||||
|
WritableNativeArray array = new WritableNativeArray();
|
||||||
|
array.pushMap(Arguments.makeNativeMap(mModule.getConstants()));
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
@DoNotStrip
|
||||||
|
public boolean supportsWebWorkers() {
|
||||||
|
return mModule.supportsWebWorkers();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DoNotStrip
|
||||||
|
public void invoke(ExecutorToken token, int methodId, ReadableNativeArray parameters) {
|
||||||
|
if (mMethods == null || methodId >= mMethods.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mMethods.get(methodId).invoke(mCatalystInstance, token, parameters);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/**
|
||||||
|
* 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.cxxbridge;
|
||||||
|
|
||||||
|
import com.facebook.jni.HybridData;
|
||||||
|
import com.facebook.proguard.annotations.DoNotStrip;
|
||||||
|
|
||||||
|
@DoNotStrip
|
||||||
|
public abstract class JavaScriptExecutor {
|
||||||
|
public interface Factory {
|
||||||
|
JavaScriptExecutor create() throws Exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final HybridData mHybridData;
|
||||||
|
|
||||||
|
protected JavaScriptExecutor(HybridData hybridData) {
|
||||||
|
mHybridData = hybridData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close this executor and cleanup any resources that it was using. No further calls are
|
||||||
|
* expected after this.
|
||||||
|
* TODO mhorowitz: This may no longer be used; check and delete if possible.
|
||||||
|
*/
|
||||||
|
public void close() {
|
||||||
|
mHybridData.resetNative();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/**
|
||||||
|
* 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.cxxbridge;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import com.facebook.jni.HybridData;
|
||||||
|
|
||||||
|
public class ModuleRegistryHolder {
|
||||||
|
private final HybridData mHybridData;
|
||||||
|
private static native HybridData initHybrid(
|
||||||
|
CatalystInstanceImpl catalystInstanceImpl,
|
||||||
|
Collection<JavaModuleWrapper> javaModules,
|
||||||
|
Collection<CxxModuleWrapper> cxxModules);
|
||||||
|
|
||||||
|
public ModuleRegistryHolder(CatalystInstanceImpl catalystInstanceImpl,
|
||||||
|
Collection<JavaModuleWrapper> javaModules,
|
||||||
|
Collection<CxxModuleWrapper> cxxModules) {
|
||||||
|
mHybridData = initHybrid(catalystInstanceImpl, javaModules, cxxModules);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
/**
|
||||||
|
* 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.cxxbridge;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.BaseJavaModule;
|
||||||
|
import com.facebook.react.bridge.CatalystInstance;
|
||||||
|
import com.facebook.react.bridge.NativeModule;
|
||||||
|
import com.facebook.react.bridge.OnBatchCompleteListener;
|
||||||
|
import com.facebook.react.bridge.ReadableNativeArray;
|
||||||
|
import com.facebook.react.common.MapBuilder;
|
||||||
|
import com.facebook.react.common.SetBuilder;
|
||||||
|
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 Map<Class<NativeModule>, NativeModule> mModuleInstances;
|
||||||
|
private final ArrayList<OnBatchCompleteListener> mBatchCompleteListenerModules;
|
||||||
|
|
||||||
|
private NativeModuleRegistry(Map<Class<NativeModule>, NativeModule> moduleInstances) {
|
||||||
|
mModuleInstances = moduleInstances;
|
||||||
|
mBatchCompleteListenerModules = new ArrayList<OnBatchCompleteListener>(mModuleInstances.size());
|
||||||
|
for (NativeModule module : mModuleInstances.values()) {
|
||||||
|
if (module instanceof OnBatchCompleteListener) {
|
||||||
|
mBatchCompleteListenerModules.add((OnBatchCompleteListener) module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ ModuleRegistryHolder getModuleRegistryHolder(
|
||||||
|
CatalystInstanceImpl catalystInstanceImpl) {
|
||||||
|
ArrayList<JavaModuleWrapper> javaModules = new ArrayList<>();
|
||||||
|
ArrayList<CxxModuleWrapper> cxxModules = new ArrayList<>();
|
||||||
|
for (NativeModule module : mModuleInstances.values()) {
|
||||||
|
if (module instanceof BaseJavaModule) {
|
||||||
|
javaModules.add(new JavaModuleWrapper(catalystInstanceImpl, (BaseJavaModule) module));
|
||||||
|
} else if (module instanceof CxxModuleWrapper) {
|
||||||
|
cxxModules.add((CxxModuleWrapper) module);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unknown module type " + module.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ModuleRegistryHolder(catalystInstanceImpl, javaModules, cxxModules);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onBatchComplete() {
|
||||||
|
for (int i = 0; i < mBatchCompleteListenerModules.size(); i++) {
|
||||||
|
mBatchCompleteListenerModules.get(i).onBatchComplete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
||||||
|
Map<Class<NativeModule>, NativeModule> moduleInstances = new HashMap<>();
|
||||||
|
for (NativeModule module : mModules.values()) {
|
||||||
|
moduleInstances.put((Class<NativeModule>)module.getClass(), module);
|
||||||
|
}
|
||||||
|
return new NativeModuleRegistry(moduleInstances);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
/**
|
||||||
|
* 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.cxxbridge;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.facebook.jni.HybridData;
|
||||||
|
import com.facebook.soloader.SoLoader;
|
||||||
|
import com.facebook.proguard.annotations.DoNotStrip;
|
||||||
|
import com.facebook.react.bridge.JavaJSExecutor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 {
|
||||||
|
public static class Factory implements JavaScriptExecutor.Factory {
|
||||||
|
private final JavaJSExecutor.Factory mJavaJSExecutorFactory;
|
||||||
|
|
||||||
|
public Factory(JavaJSExecutor.Factory javaJSExecutorFactory) {
|
||||||
|
mJavaJSExecutorFactory = javaJSExecutorFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaScriptExecutor create() throws Exception {
|
||||||
|
return new ProxyJavaScriptExecutor(mJavaJSExecutorFactory.create());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
SoLoader.loadLibrary(CatalystInstanceImpl.REACT_NATIVE_LIB);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
super(initHybrid(executor));
|
||||||
|
mJavaJSExecutor = executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if (mJavaJSExecutor != null) {
|
||||||
|
mJavaJSExecutor.close();
|
||||||
|
mJavaJSExecutor = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private native static HybridData initHybrid(JavaJSExecutor executor);
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* 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.cxxbridge;
|
||||||
|
|
||||||
|
import com.facebook.proguard.annotations.DoNotStrip;
|
||||||
|
|
||||||
|
@DoNotStrip
|
||||||
|
/* package */ interface ReactCallback {
|
||||||
|
@DoNotStrip
|
||||||
|
void onBatchComplete();
|
||||||
|
|
||||||
|
@DoNotStrip
|
||||||
|
void incrementPendingJSCalls();
|
||||||
|
|
||||||
|
@DoNotStrip
|
||||||
|
void decrementPendingJSCalls();
|
||||||
|
|
||||||
|
@DoNotStrip
|
||||||
|
void onNativeException(Exception e);
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
|
package com.facebook.react.cxxbridge;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import com.facebook.proguard.annotations.DoNotStrip;
|
||||||
|
/**
|
||||||
|
* Static class that allows markers to be placed in React code and responded to in a
|
||||||
|
* configurable way
|
||||||
|
*/
|
||||||
|
@DoNotStrip
|
||||||
|
public class ReactMarker {
|
||||||
|
|
||||||
|
public interface MarkerListener {
|
||||||
|
void logMarker(String name);
|
||||||
|
};
|
||||||
|
|
||||||
|
@Nullable static private MarkerListener sMarkerListener = null;
|
||||||
|
|
||||||
|
static public void setMarkerListener(MarkerListener listener) {
|
||||||
|
sMarkerListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@DoNotStrip
|
||||||
|
static public void logMarker(String name) {
|
||||||
|
if (sMarkerListener != null) {
|
||||||
|
sMarkerListener.logMarker(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/**
|
||||||
|
* 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.cxxbridge;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.AssertionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class to make assertions that should not hard-crash the app but instead be handled by the
|
||||||
|
* Catalyst app {@link NativeModuleCallExceptionHandler}. See the javadoc on that class for
|
||||||
|
* more information about our opinion on when these assertions should be used as opposed to
|
||||||
|
* assertions that might throw AssertionError Throwables that will cause the app to hard crash.
|
||||||
|
*/
|
||||||
|
public class SoftAssertions {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throw {@link AssertionException} with a given message. Use this method surrounded with
|
||||||
|
* {@code if} block with assert condition in case you plan to do string concatenation to produce
|
||||||
|
* the message.
|
||||||
|
*/
|
||||||
|
public static void assertUnreachable(String message) {
|
||||||
|
throw new AssertionException(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the given condition, throwing an {@link AssertionException} if the condition doesn't
|
||||||
|
* hold.
|
||||||
|
*/
|
||||||
|
public static void assertCondition(boolean condition, String message) {
|
||||||
|
if (!condition) {
|
||||||
|
throw new AssertionException(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the given Object isn't null, throwing an {@link AssertionException} if it was.
|
||||||
|
*/
|
||||||
|
public static <T> T assertNotNull(@Nullable T instance) {
|
||||||
|
if (instance == null) {
|
||||||
|
throw new AssertionException("Expected object to not be null!");
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/**
|
||||||
|
* 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.cxxbridge;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility for interacting with the UI thread.
|
||||||
|
*/
|
||||||
|
public class UiThreadUtil {
|
||||||
|
|
||||||
|
@Nullable private static Handler sMainHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@code true} if the current thread is the UI thread.
|
||||||
|
*/
|
||||||
|
public static boolean isOnUiThread() {
|
||||||
|
return Looper.getMainLooper().getThread() == Thread.currentThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws an {@link AssertionException} if the current thread is not the UI thread.
|
||||||
|
*/
|
||||||
|
public static void assertOnUiThread() {
|
||||||
|
SoftAssertions.assertCondition(isOnUiThread(), "Expected to run on UI thread!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws an {@link AssertionException} if the current thread is the UI thread.
|
||||||
|
*/
|
||||||
|
public static void assertNotOnUiThread() {
|
||||||
|
SoftAssertions.assertCondition(!isOnUiThread(), "Expected not to run on UI thread!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the given {@code Runnable} on the UI thread.
|
||||||
|
*/
|
||||||
|
public static void runOnUiThread(Runnable runnable) {
|
||||||
|
synchronized (UiThreadUtil.class) {
|
||||||
|
if (sMainHandler == null) {
|
||||||
|
sMainHandler = new Handler(Looper.getMainLooper());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sMainHandler.post(runnable);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
## Putting this here is kind of a hack. I don't want to modify the OSS bridge.
|
||||||
|
## TODO mhorowitz: add @DoNotStrip to the interface directly.
|
||||||
|
|
||||||
|
-keepclassmembers class com.facebook.react.bridge.queue.MessageQueueThread {
|
||||||
|
public boolean isOnThread();
|
||||||
|
public void assertIsOnThread();
|
||||||
|
}
|
Loading…
Reference in New Issue