Permalink
Browse files

Queue JS calls that come in before JS bundle has started loading inst…

…ead of crashing

Summary: This mimics (some of) the behavior we have on iOS where if you call a JS module method before the JS bundle has started loading, we just queue up those calls and execute them after the bundle has started loading.

Reviewed By: javache

Differential Revision: D4117581

fbshipit-source-id: 58c5a6f87aeeb86083385334d92f2716a0574ba1
  • Loading branch information...
1 parent 52d90da commit 68aeffe01f442a67a53dd0eff43e9357b0ac3b22 @astreet astreet committed with Facebook Github Bot Nov 3, 2016
@@ -40,7 +40,6 @@ void callFunction(
*/
void destroy();
boolean isDestroyed();
- boolean isAcceptingCalls();
/**
* Initialize all the native modules
@@ -40,7 +40,8 @@
private static final String EARLY_JS_ACCESS_EXCEPTION_MESSAGE =
"Tried to access a JS module before the React instance was fully set up. Calls to " +
- "ReactContext#getJSModule should be protected by ReactContext#hasActiveCatalystInstance().";
+ "ReactContext#getJSModule should only happen once initialize() has been called on your " +
+ "native module.";
private final CopyOnWriteArraySet<LifecycleEventListener> mLifecycleEventListeners =
new CopyOnWriteArraySet<>();
@@ -143,7 +144,7 @@ public CatalystInstance getCatalystInstance() {
}
public boolean hasActiveCatalystInstance() {
- return mCatalystInstance != null && mCatalystInstance.isAcceptingCalls();
+ return mCatalystInstance != null && !mCatalystInstance.isDestroyed();
}
public LifecycleState getLifecycleState() {
@@ -12,6 +12,7 @@
import javax.annotation.Nullable;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
@@ -57,6 +58,25 @@
private static final AtomicInteger sNextInstanceIdForTrace = new AtomicInteger(1);
+ private static class PendingJSCall {
+
+ public ExecutorToken mExecutorToken;
+ public String mModule;
+ public String mMethod;
+ public NativeArray mArguments;
+
+ public PendingJSCall(
+ ExecutorToken executorToken,
+ String module,
+ String method,
+ NativeArray arguments) {
+ mExecutorToken = executorToken;
+ mModule = module;
+ mMethod = method;
+ mArguments = arguments;
+ }
+ }
+
// Access from any thread
private final ReactQueueConfigurationImpl mReactQueueConfiguration;
private final CopyOnWriteArrayList<NotThreadSafeBridgeIdleDebugListener> mBridgeIdleListeners;
@@ -67,6 +87,8 @@
private final TraceListener mTraceListener;
private final JavaScriptModuleRegistry mJSModuleRegistry;
private final JSBundleLoader mJSBundleLoader;
+ private final ArrayList<PendingJSCall> mJSCallsPendingInit = new ArrayList<PendingJSCall>();
+ private final Object mJSCallsPendingInitLock = new Object();
private ExecutorToken mMainExecutorToken;
private final NativeModuleRegistry mJavaRegistry;
@@ -168,10 +190,20 @@ public void runJSBundle() {
mJSBundleHasLoaded = true;
// incrementPendingJSCalls();
mJSBundleLoader.loadScript(CatalystInstanceImpl.this);
- // Loading the bundle is queued on the JS thread, but may not have
- // run yet. It's save to set this here, though, since any work it
- // gates will be queued on the JS thread behind the load.
- mAcceptCalls = true;
+
+ synchronized (mJSCallsPendingInitLock) {
+ // Loading the bundle is queued on the JS thread, but may not have
+ // run yet. It's save to set this here, though, since any work it
+ // gates will be queued on the JS thread behind the load.
+ mAcceptCalls = true;
+
+ for (PendingJSCall call : mJSCallsPendingInit) {
+ callJSFunction(call.mExecutorToken, call.mModule, call.mMethod, call.mArguments);
+ }
+ mJSCallsPendingInit.clear();
+ }
+
+
// This is registered after JS starts since it makes a JS call
Systrace.registerListener(mTraceListener);
}
@@ -193,7 +225,13 @@ public void callFunction(
return;
}
if (!mAcceptCalls) {
- throw new RuntimeException("Attempt to call JS function before JS bundle is loaded.");
+ // Most of the time the instance is initialized and we don't need to acquire the lock
+ synchronized (mJSCallsPendingInitLock) {
+ if (!mAcceptCalls) {
+ mJSCallsPendingInit.add(new PendingJSCall(executorToken, module, method, arguments));
+ return;
+ }
+ }
}
callJSFunction(executorToken, module, method, arguments);
@@ -244,11 +282,6 @@ public boolean isDestroyed() {
return mDestroyed;
}
- @Override
- public boolean isAcceptingCalls() {
- return !mDestroyed && mAcceptCalls;
- }
-
/**
* Initialize all the native modules
*/

0 comments on commit 68aeffe

Please sign in to comment.