Allow reactinstancemanager to set an initialization function

Reviewed By: javache

Differential Revision: D5227811

fbshipit-source-id: e7868481de2a8799af5d6a6bcad26369d054b35e
This commit is contained in:
Kathy Gray 2017-08-14 06:50:30 -07:00 committed by Facebook Github Bot
parent 6783694158
commit 84e80eb781
4 changed files with 70 additions and 25 deletions

View File

@ -55,6 +55,7 @@ import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactMarker;
import com.facebook.react.bridge.ReactMarkerConstants;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.bridge.NativeArray;
import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.common.ReactConstants;
@ -105,6 +106,8 @@ import javax.annotation.Nullable;
@ThreadSafe
public class ReactInstanceManager {
private static final String TAG = ReactInstanceManager.class.getSimpleName();
/**
* Listener interface for react instance events.
*/
@ -127,6 +130,7 @@ public class ReactInstanceManager {
private final @Nullable JSBundleLoader mBundleLoader; /* path to JS bundle on file system */
private final @Nullable String mJSMainModulePath; /* path to JS bundle root on packager server */
private final List<ReactPackage> mPackages;
private final List<CatalystInstanceImpl.PendingJSCall> mInitFunctions;
private final DevSupportManager mDevSupportManager;
private final boolean mUseDeveloperSupport;
private final @Nullable NotThreadSafeBridgeIdleDebugListener mBridgeIdleDebugListener;
@ -232,6 +236,7 @@ public class ReactInstanceManager {
mBundleLoader = bundleLoader;
mJSMainModulePath = jsMainModulePath;
mPackages = new ArrayList<>();
mInitFunctions = new ArrayList<>();
mUseDeveloperSupport = useDeveloperSupport;
mDevSupportManager = DevSupportManagerFactory.create(
applicationContext,
@ -351,11 +356,33 @@ public class ReactInstanceManager {
}
/**
* 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.
* If the JavaScript bundle for this app requires initialization as part of bridge start up,
* register a function using its @param module and @param method and optional arguments.
*/
public void registerInitFunction(String module, String method, @Nullable NativeArray arguments) {
CatalystInstanceImpl.PendingJSCall init =
new CatalystInstanceImpl.PendingJSCall(module, method, arguments);
synchronized (this) {
mInitFunctions.add(init);
}
ReactContext context = getCurrentReactContext();
CatalystInstance catalystInstance = context == null ? null : context.getCatalystInstance();
if (catalystInstance == null) {
return;
} else {
// CatalystInstance is only visible after running jsBundle, so these will be put on the native
// JS queue
// TODO T20546472 remove cast when catalystInstance and InstanceImpl are renamed/merged
((CatalystInstanceImpl) catalystInstance).callFunction(init);
}
}
/**
* 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.
* <p>Called from UI thread.
*/
@ThreadConfined(UI)
public void recreateReactContextInBackground() {
@ -995,10 +1022,19 @@ public class ReactInstanceManager {
if (Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JSC_CALLS)) {
catalystInstance.setGlobalVariable("__RCTProfileIsProfiling", "true");
}
catalystInstance.runJSBundle();
// Transitions functions in the minitFunctions list to catalystInstance, to run after the bundle
// TODO T20546472
if (!mInitFunctions.isEmpty()) {
for (CatalystInstanceImpl.PendingJSCall function : mInitFunctions) {
((CatalystInstanceImpl) catalystInstance).callFunction(function);
}
}
ReactMarker.logMarker(ReactMarkerConstants.PRE_RUN_JS_BUNDLE_START);
reactContext.initializeWithInstance(catalystInstance);
ReactMarker.logMarker(ReactMarkerConstants.PRE_RUN_JS_BUNDLE_START);
catalystInstance.runJSBundle();
return reactContext;
}

View File

@ -43,20 +43,27 @@ public class CatalystInstanceImpl implements CatalystInstance {
private static final AtomicInteger sNextInstanceIdForTrace = new AtomicInteger(1);
private static class PendingJSCall {
public static class PendingJSCall {
public String mModule;
public String mMethod;
public NativeArray mArguments;
public @Nullable NativeArray mArguments;
public PendingJSCall(
String module,
String method,
NativeArray arguments) {
public PendingJSCall(String module, String method, @Nullable NativeArray arguments) {
mModule = module;
mMethod = method;
mArguments = arguments;
}
void call(CatalystInstanceImpl catalystInstance) {
NativeArray arguments = mArguments != null ? mArguments : new WritableNativeArray();
catalystInstance.jniCallJSFunction(mModule, mMethod, arguments);
}
public String toString() {
return mModule + "." + mMethod + "("
+ (mArguments == null ? "" : mArguments.toString()) + ")";
}
}
// Access from any thread
@ -216,7 +223,6 @@ public class CatalystInstanceImpl implements CatalystInstance {
public void runJSBundle() {
Log.d(ReactConstants.TAG, "CatalystInstanceImpl.runJSBundle()");
Assertions.assertCondition(!mJSBundleHasLoaded, "JS bundle was already loaded!");
// incrementPendingJSCalls();
mJSBundleLoader.loadScript(CatalystInstanceImpl.this);
@ -227,8 +233,8 @@ public class CatalystInstanceImpl implements CatalystInstance {
// gates will be queued on the JS thread behind the load.
mAcceptCalls = true;
for (PendingJSCall call : mJSCallsPendingInit) {
jniCallJSFunction(call.mModule, call.mMethod, call.mArguments);
for (PendingJSCall function : mJSCallsPendingInit) {
function.call(this);
}
mJSCallsPendingInit.clear();
mJSBundleHasLoaded = true;
@ -260,8 +266,12 @@ public class CatalystInstanceImpl implements CatalystInstance {
final String module,
final String method,
final NativeArray arguments) {
callFunction(new PendingJSCall(module, method, arguments));
}
public void callFunction(PendingJSCall function) {
if (mDestroyed) {
final String call = module + "." + method + "(" + arguments.toString() + ")";
final String call = function.toString();
FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed: " + call);
return;
}
@ -269,13 +279,12 @@ public class CatalystInstanceImpl implements CatalystInstance {
// 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(module, method, arguments));
mJSCallsPendingInit.add(function);
return;
}
}
}
jniCallJSFunction(module, method, arguments);
function.call(this);
}
private native void jniCallJSCallback(int callbackID, NativeArray arguments);

View File

@ -186,14 +186,13 @@ public class StackTraceHelper {
Matcher matcher = STACK_FRAME_PATTERN.matcher(stackTrace[i]);
if (!matcher.find()) {
throw new IllegalArgumentException(
"Unexpected stack frame format: " + stackTrace[i]);
"Unexpected stack frame format: " + stackTrace[i]);
}
result[i] = new StackFrameImpl(
matcher.group(2),
matcher.group(1) == null ? "(unknown)" : matcher.group(1),
Integer.parseInt(matcher.group(3)),
Integer.parseInt(matcher.group(4)));
matcher.group(2),
matcher.group(1) == null ? "(unknown)" : matcher.group(1),
Integer.parseInt(matcher.group(3)),
Integer.parseInt(matcher.group(4)));
}
}
return result;

View File

@ -88,6 +88,7 @@ public class ReactModuleSpecProcessor extends AbstractProcessor {
TypeElement typeElement = (TypeElement) reactModuleListElement;
ReactModuleList reactModuleList = typeElement.getAnnotation(ReactModuleList.class);
if (reactModuleList == null) {
continue;
}