diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java index 37822b8d6..729fadaee 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java @@ -533,6 +533,7 @@ public class ReactInstanceManager { } reactContext.initializeWithInstance(catalystInstance); + catalystInstance.runJSBundle(); return reactContext; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java index 75cfb0588..017c7f087 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java @@ -42,6 +42,7 @@ import com.fasterxml.jackson.core.JsonGenerator; public class CatalystInstance { private static final int BRIDGE_SETUP_TIMEOUT_MS = 15000; + private static final int LOAD_JS_BUNDLE_TIMEOUT_MS = 15000; private static final AtomicInteger sNextInstanceIdForTrace = new AtomicInteger(1); @@ -52,6 +53,9 @@ public class CatalystInstance { 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; // Access from native modules thread private final NativeModuleRegistry mJavaRegistry; @@ -60,8 +64,7 @@ public class CatalystInstance { // Access from JS thread private @Nullable ReactBridge mBridge; - private @Nullable JavaScriptModuleRegistry mJSModuleRegistry; - private @Nullable TraceListener mTraceListener; + private boolean mJSBundleHasLoaded; private CatalystInstance( final CatalystQueueConfigurationSpec catalystQueueConfigurationSpec, @@ -73,19 +76,20 @@ public class CatalystInstance { mCatalystQueueConfiguration = CatalystQueueConfiguration.create( catalystQueueConfigurationSpec, new NativeExceptionHandler()); - mBridgeIdleListeners = new CopyOnWriteArrayList(); + mBridgeIdleListeners = new CopyOnWriteArrayList<>(); mJavaRegistry = registry; + mJSModuleRegistry = new JavaScriptModuleRegistry(CatalystInstance.this, jsModulesConfig); + mJSBundleLoader = jsBundleLoader; mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler; + mTraceListener = new JSProfilerTraceListener(); + Systrace.registerListener(mTraceListener); final CountDownLatch initLatch = new CountDownLatch(1); mCatalystQueueConfiguration.getJSQueueThread().runOnQueue( new Runnable() { @Override public void run() { - initializeBridge(jsExecutor, registry, jsModulesConfig, jsBundleLoader); - mJSModuleRegistry = - new JavaScriptModuleRegistry(CatalystInstance.this, jsModulesConfig); - + initializeBridge(jsExecutor, jsModulesConfig); initLatch.countDown(); } }); @@ -101,65 +105,48 @@ public class CatalystInstance { private void initializeBridge( JavaScriptExecutor jsExecutor, - NativeModuleRegistry registry, - JavaScriptModulesConfig jsModulesConfig, - JSBundleLoader jsBundleLoader) { + JavaScriptModulesConfig jsModulesConfig) { mCatalystQueueConfiguration.getJSQueueThread().assertIsOnThread(); Assertions.assertCondition(mBridge == null, "initializeBridge should be called once"); + mBridge = new ReactBridge( jsExecutor, new NativeModulesReactCallback(), mCatalystQueueConfiguration.getNativeModulesQueueThread()); mBridge.setGlobalVariable( "__fbBatchedBridgeConfig", - buildModulesConfigJSONProperty(registry, jsModulesConfig)); + buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig)); + } + + public void runJSBundle() { Systrace.beginSection( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, - "CatalystInstance_initializeBridge"); + "CatalystInstance_runJSBundle"); + try { - jsBundleLoader.loadScript(mBridge); + final CountDownLatch initLatch = new CountDownLatch(1); + mCatalystQueueConfiguration.getJSQueueThread().runOnQueue( + new Runnable() { + @Override + public void run() { + Assertions.assertCondition(!mJSBundleHasLoaded, "JS bundle was already loaded!"); + mJSBundleHasLoaded = true; + + incrementPendingJSCalls(); + + mJSBundleLoader.loadScript(mBridge); + + initLatch.countDown(); + } + }); + Assertions.assertCondition( + initLatch.await(LOAD_JS_BUNDLE_TIMEOUT_MS, TimeUnit.MILLISECONDS), + "Timed out loading JS!"); + } catch (InterruptedException e) { + throw new RuntimeException(e); } finally { Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); } - - mTraceListener = new TraceListener() { - @Override - public void onTraceStarted() { - mCatalystQueueConfiguration.getJSQueueThread().runOnQueue( - new Runnable() { - @Override - public void run() { - mCatalystQueueConfiguration.getJSQueueThread().assertIsOnThread(); - - if (mDestroyed) { - return; - } - Assertions.assertNotNull(mBridge).setGlobalVariable( - "__BridgeProfilingIsProfiling", - "true"); - } - }); - } - - @Override - public void onTraceStopped() { - mCatalystQueueConfiguration.getJSQueueThread().runOnQueue( - new Runnable() { - @Override - public void run() { - mCatalystQueueConfiguration.getJSQueueThread().assertIsOnThread(); - - if (mDestroyed) { - return; - } - Assertions.assertNotNull(mBridge).setGlobalVariable( - "__BridgeProfilingIsProfiling", - "false"); - } - }); - } - }; - Systrace.registerListener(mTraceListener); } /* package */ void callFunction( @@ -351,6 +338,7 @@ public class CatalystInstance { private void decrementPendingJSCalls() { int newPendingCalls = mPendingJSCalls.decrementAndGet(); + Assertions.assertCondition(newPendingCalls >= 0); boolean isNowIdle = newPendingCalls == 0; Systrace.traceCounter( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, @@ -417,6 +405,44 @@ public class CatalystInstance { } } + private class JSProfilerTraceListener implements TraceListener { + @Override + public void onTraceStarted() { + mCatalystQueueConfiguration.getJSQueueThread().runOnQueue( + new Runnable() { + @Override + public void run() { + mCatalystQueueConfiguration.getJSQueueThread().assertIsOnThread(); + + if (mDestroyed) { + return; + } + Assertions.assertNotNull(mBridge).setGlobalVariable( + "__BridgeProfilingIsProfiling", + "true"); + } + }); + } + + @Override + public void onTraceStopped() { + mCatalystQueueConfiguration.getJSQueueThread().runOnQueue( + new Runnable() { + @Override + public void run() { + mCatalystQueueConfiguration.getJSQueueThread().assertIsOnThread(); + + if (mDestroyed) { + return; + } + Assertions.assertNotNull(mBridge).setGlobalVariable( + "__BridgeProfilingIsProfiling", + "false"); + } + }); + } + } + public static class Builder { private @Nullable CatalystQueueConfigurationSpec mCatalystQueueConfigurationSpec; diff --git a/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp b/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp index 2ee64145d..813fb382a 100644 --- a/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp +++ b/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp @@ -618,6 +618,19 @@ static void create(JNIEnv* env, jobject obj, jobject executor, jobject callback, setCountableForJava(env, obj, std::move(bridge)); } +static void executeApplicationScript( + const RefPtr& bridge, + const std::string script, + const std::string sourceUri) { + try { + // Execute the application script and collect/dispatch any native calls that might have occured + bridge->executeApplicationScript(script, sourceUri); + bridge->executeJSCall("BatchedBridge", "flushedQueue", std::vector()); + } catch (...) { + translatePendingCppExceptionToJavaException(); + } +} + static void loadScriptFromAssets(JNIEnv* env, jobject obj, jobject assetManager, jstring assetName) { jclass markerClass = env->FindClass("com/facebook/react/bridge/ReactMarker"); @@ -634,7 +647,7 @@ static void loadScriptFromAssets(JNIEnv* env, jobject obj, jobject assetManager, #endif env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, env->NewStringUTF("loadScriptFromNetworkCached_read")); - bridge->executeApplicationScript(script, assetNameStr); + executeApplicationScript(bridge, script, assetNameStr); env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, env->NewStringUTF("loadScriptFromNetworkCached_done")); } @@ -655,7 +668,7 @@ static void loadScriptFromNetworkCached(JNIEnv* env, jobject obj, jstring source "sourceURL", sourceURLStr); #endif env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, env->NewStringUTF("loadScriptFromNetworkCached_read")); - bridge->executeApplicationScript(script, jni::fromJString(env, sourceURL)); + executeApplicationScript(bridge, script, jni::fromJString(env, sourceURL)); env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, env->NewStringUTF("loadScriptFromNetworkCached_exec")); }