mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-11 11:29:03 +08:00
WebWorkers: Add ExecutorToken to route native module calls to/from workers
Summary:To support native modules in web workers, native modules need to have an notion of the JS executor/thread that called into them in order to respond via Callback or JS module call to the right executor on the right thread. ExecutorToken is an object that only serves as a token that can be used to identify an executor/message queue thread in the bridge. It doesn't expose any methods. Native modules Callback objects automatically have this ExecutionContext attached -- JSModule calls for modules that support workers will need to supply an appropriate ExecutorToken when retrieving the JSModule implementation from the ReactContext. Reviewed By: mhorowitz Differential Revision: D2965458 fb-gh-sync-id: 6e354d4df8536d40b12d02bd055f6d06b4ca595d shipit-source-id: 6e354d4df8536d40b12d02bd055f6d06b4ca595d
This commit is contained in:
committed by
Facebook Github Bot 4
parent
f67fa82008
commit
bd95b22844
@@ -57,14 +57,14 @@ public abstract class BaseJavaModule implements NativeModule {
|
||||
}
|
||||
|
||||
public abstract @Nullable T extractArgument(
|
||||
CatalystInstance catalystInstance, ReadableNativeArray jsArguments, int atIndex);
|
||||
CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex);
|
||||
}
|
||||
|
||||
static final private ArgumentExtractor<Boolean> ARGUMENT_EXTRACTOR_BOOLEAN =
|
||||
new ArgumentExtractor<Boolean>() {
|
||||
@Override
|
||||
public Boolean extractArgument(
|
||||
CatalystInstance catalystInstance, ReadableNativeArray jsArguments, int atIndex) {
|
||||
CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) {
|
||||
return jsArguments.getBoolean(atIndex);
|
||||
}
|
||||
};
|
||||
@@ -73,7 +73,7 @@ public abstract class BaseJavaModule implements NativeModule {
|
||||
new ArgumentExtractor<Double>() {
|
||||
@Override
|
||||
public Double extractArgument(
|
||||
CatalystInstance catalystInstance, ReadableNativeArray jsArguments, int atIndex) {
|
||||
CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) {
|
||||
return jsArguments.getDouble(atIndex);
|
||||
}
|
||||
};
|
||||
@@ -82,7 +82,7 @@ public abstract class BaseJavaModule implements NativeModule {
|
||||
new ArgumentExtractor<Float>() {
|
||||
@Override
|
||||
public Float extractArgument(
|
||||
CatalystInstance catalystInstance, ReadableNativeArray jsArguments, int atIndex) {
|
||||
CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) {
|
||||
return (float) jsArguments.getDouble(atIndex);
|
||||
}
|
||||
};
|
||||
@@ -91,7 +91,7 @@ public abstract class BaseJavaModule implements NativeModule {
|
||||
new ArgumentExtractor<Integer>() {
|
||||
@Override
|
||||
public Integer extractArgument(
|
||||
CatalystInstance catalystInstance, ReadableNativeArray jsArguments, int atIndex) {
|
||||
CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) {
|
||||
return (int) jsArguments.getDouble(atIndex);
|
||||
}
|
||||
};
|
||||
@@ -100,7 +100,7 @@ public abstract class BaseJavaModule implements NativeModule {
|
||||
new ArgumentExtractor<String>() {
|
||||
@Override
|
||||
public String extractArgument(
|
||||
CatalystInstance catalystInstance, ReadableNativeArray jsArguments, int atIndex) {
|
||||
CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) {
|
||||
return jsArguments.getString(atIndex);
|
||||
}
|
||||
};
|
||||
@@ -109,7 +109,7 @@ public abstract class BaseJavaModule implements NativeModule {
|
||||
new ArgumentExtractor<ReadableNativeArray>() {
|
||||
@Override
|
||||
public ReadableNativeArray extractArgument(
|
||||
CatalystInstance catalystInstance, ReadableNativeArray jsArguments, int atIndex) {
|
||||
CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) {
|
||||
return jsArguments.getArray(atIndex);
|
||||
}
|
||||
};
|
||||
@@ -118,7 +118,7 @@ public abstract class BaseJavaModule implements NativeModule {
|
||||
new ArgumentExtractor<ReadableMap>() {
|
||||
@Override
|
||||
public ReadableMap extractArgument(
|
||||
CatalystInstance catalystInstance, ReadableNativeArray jsArguments, int atIndex) {
|
||||
CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) {
|
||||
return jsArguments.getMap(atIndex);
|
||||
}
|
||||
};
|
||||
@@ -127,12 +127,12 @@ public abstract class BaseJavaModule implements NativeModule {
|
||||
new ArgumentExtractor<Callback>() {
|
||||
@Override
|
||||
public @Nullable Callback extractArgument(
|
||||
CatalystInstance catalystInstance, ReadableNativeArray jsArguments, int atIndex) {
|
||||
CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) {
|
||||
if (jsArguments.isNull(atIndex)) {
|
||||
return null;
|
||||
} else {
|
||||
int id = (int) jsArguments.getDouble(atIndex);
|
||||
return new CallbackImpl(catalystInstance, id);
|
||||
return new CallbackImpl(catalystInstance, executorToken, id);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -146,11 +146,11 @@ public abstract class BaseJavaModule implements NativeModule {
|
||||
|
||||
@Override
|
||||
public Promise extractArgument(
|
||||
CatalystInstance catalystInstance, ReadableNativeArray jsArguments, int atIndex) {
|
||||
CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) {
|
||||
Callback resolve = ARGUMENT_EXTRACTOR_CALLBACK
|
||||
.extractArgument(catalystInstance, jsArguments, atIndex);
|
||||
.extractArgument(catalystInstance, executorToken, jsArguments, atIndex);
|
||||
Callback reject = ARGUMENT_EXTRACTOR_CALLBACK
|
||||
.extractArgument(catalystInstance, jsArguments, atIndex + 1);
|
||||
.extractArgument(catalystInstance, executorToken, jsArguments, atIndex + 1);
|
||||
return new PromiseImpl(resolve, reject);
|
||||
}
|
||||
};
|
||||
@@ -174,9 +174,21 @@ public abstract class BaseJavaModule implements NativeModule {
|
||||
}
|
||||
|
||||
private ArgumentExtractor[] buildArgumentExtractors(Class[] paramTypes) {
|
||||
ArgumentExtractor[] argumentExtractors = new ArgumentExtractor[paramTypes.length];
|
||||
for (int i = 0; i < paramTypes.length; i += argumentExtractors[i].getJSArgumentsNeeded()) {
|
||||
Class argumentClass = paramTypes[i];
|
||||
// Modules that support web workers are expected to take an ExecutorToken as the first
|
||||
// parameter to all their @ReactMethod-annotated methods. We compensate for that here.
|
||||
int executorTokenOffset = 0;
|
||||
if (BaseJavaModule.this.supportsWebWorkers()) {
|
||||
if (paramTypes[0] != ExecutorToken.class) {
|
||||
throw new RuntimeException(
|
||||
"Module " + BaseJavaModule.this + " supports web workers, but " + mMethod.getName() +
|
||||
"does not take an ExecutorToken as its first parameter.");
|
||||
}
|
||||
executorTokenOffset = 1;
|
||||
}
|
||||
|
||||
ArgumentExtractor[] argumentExtractors = new ArgumentExtractor[paramTypes.length - executorTokenOffset];
|
||||
for (int i = 0; i < paramTypes.length - executorTokenOffset; i += argumentExtractors[i].getJSArgumentsNeeded()) {
|
||||
Class argumentClass = paramTypes[i + executorTokenOffset];
|
||||
if (argumentClass == Boolean.class || argumentClass == boolean.class) {
|
||||
argumentExtractors[i] = ARGUMENT_EXTRACTOR_BOOLEAN;
|
||||
} else if (argumentClass == Integer.class || argumentClass == int.class) {
|
||||
@@ -220,7 +232,7 @@ public abstract class BaseJavaModule implements NativeModule {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(CatalystInstance catalystInstance, ReadableNativeArray parameters) {
|
||||
public void invoke(CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray parameters) {
|
||||
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "callJavaModuleMethod");
|
||||
try {
|
||||
if (mJSArgumentsNeeded != parameters.size()) {
|
||||
@@ -229,11 +241,18 @@ public abstract class BaseJavaModule implements NativeModule {
|
||||
parameters.size() + " arguments, expected " + mJSArgumentsNeeded);
|
||||
}
|
||||
|
||||
// Modules that support web workers are expected to take an ExecutorToken as the first
|
||||
// parameter to all their @ReactMethod-annotated methods. We compensate for that here.
|
||||
int i = 0, jsArgumentsConsumed = 0;
|
||||
int executorTokenOffset = 0;
|
||||
if (BaseJavaModule.this.supportsWebWorkers()) {
|
||||
mArguments[0] = executorToken;
|
||||
executorTokenOffset = 1;
|
||||
}
|
||||
try {
|
||||
for (; i < mArgumentExtractors.length; i++) {
|
||||
mArguments[i] = mArgumentExtractors[i].extractArgument(
|
||||
catalystInstance, parameters, jsArgumentsConsumed);
|
||||
mArguments[i + executorTokenOffset] = mArgumentExtractors[i].extractArgument(
|
||||
catalystInstance, executorToken, parameters, jsArgumentsConsumed);
|
||||
jsArgumentsConsumed += mArgumentExtractors[i].getJSArgumentsNeeded();
|
||||
}
|
||||
} catch (UnexpectedNativeTypeException e) {
|
||||
@@ -341,7 +360,7 @@ public abstract class BaseJavaModule implements NativeModule {
|
||||
public void onCatalystInstanceDestroy() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supportsWebWorkers() {
|
||||
return false;
|
||||
|
||||
@@ -15,15 +15,17 @@ package com.facebook.react.bridge;
|
||||
public final class CallbackImpl implements Callback {
|
||||
|
||||
private final CatalystInstance mCatalystInstance;
|
||||
private final ExecutorToken mExecutorToken;
|
||||
private final int mCallbackId;
|
||||
|
||||
public CallbackImpl(CatalystInstance bridge, int callbackId) {
|
||||
public CallbackImpl(CatalystInstance bridge, ExecutorToken executorToken, int callbackId) {
|
||||
mCatalystInstance = bridge;
|
||||
mExecutorToken = executorToken;
|
||||
mCallbackId = callbackId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(Object... args) {
|
||||
mCatalystInstance.invokeCallback(mCallbackId, Arguments.fromJavaArgs(args));
|
||||
mCatalystInstance.invokeCallback(mExecutorToken, mCallbackId, Arguments.fromJavaArgs(args));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import java.util.Collection;
|
||||
import com.facebook.react.bridge.queue.ReactQueueConfiguration;
|
||||
import com.facebook.react.common.annotations.VisibleForTesting;
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
import com.facebook.react.common.annotations.VisibleForTesting;
|
||||
|
||||
/**
|
||||
* A higher level API on top of the asynchronous JSC bridge. This provides an
|
||||
@@ -27,7 +26,7 @@ public interface CatalystInstance {
|
||||
// This is called from java code, so it won't be stripped anyway, but proguard will rename it,
|
||||
// which this prevents.
|
||||
@DoNotStrip
|
||||
void invokeCallback(final int callbackID, final NativeArray arguments);
|
||||
void invokeCallback(ExecutorToken executorToken, final int callbackID, final NativeArray 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
|
||||
@@ -45,6 +44,7 @@ public interface CatalystInstance {
|
||||
ReactQueueConfiguration getReactQueueConfiguration();
|
||||
|
||||
<T extends JavaScriptModule> T getJSModule(Class<T> jsInterface);
|
||||
<T extends JavaScriptModule> T getJSModule(ExecutorToken executorToken, Class<T> jsInterface);
|
||||
<T extends NativeModule> T getNativeModule(Class<T> nativeModuleInterface);
|
||||
Collection<NativeModule> getNativeModules();
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ public class CatalystInstanceImpl implements CatalystInstance {
|
||||
private final JavaScriptModuleRegistry mJSModuleRegistry;
|
||||
private final JSBundleLoader mJSBundleLoader;
|
||||
private final Object mTeardownLock = new Object();
|
||||
private @Nullable ExecutorToken mMainExecutorToken;
|
||||
|
||||
// Access from native modules thread
|
||||
private final NativeModuleRegistry mJavaRegistry;
|
||||
@@ -113,6 +114,7 @@ public class CatalystInstanceImpl implements CatalystInstance {
|
||||
jsExecutor,
|
||||
new NativeModulesReactCallback(),
|
||||
mReactQueueConfiguration.getNativeModulesQueueThread());
|
||||
mMainExecutorToken = bridge.getMainExecutorToken();
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
}
|
||||
@@ -165,10 +167,11 @@ public class CatalystInstanceImpl implements CatalystInstance {
|
||||
}
|
||||
|
||||
/* package */ void callFunction(
|
||||
final int moduleId,
|
||||
final int methodId,
|
||||
final NativeArray arguments,
|
||||
final String tracingName) {
|
||||
ExecutorToken executorToken,
|
||||
int moduleId,
|
||||
int methodId,
|
||||
NativeArray arguments,
|
||||
String tracingName) {
|
||||
synchronized (mTeardownLock) {
|
||||
if (mDestroyed) {
|
||||
FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed.");
|
||||
@@ -177,7 +180,7 @@ public class CatalystInstanceImpl implements CatalystInstance {
|
||||
|
||||
incrementPendingJSCalls();
|
||||
|
||||
Assertions.assertNotNull(mBridge).callFunction(moduleId, methodId, arguments, tracingName);
|
||||
Assertions.assertNotNull(mBridge).callFunction(executorToken, moduleId, methodId, arguments, tracingName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +188,7 @@ public class CatalystInstanceImpl implements CatalystInstance {
|
||||
// which this prevents.
|
||||
@DoNotStrip
|
||||
@Override
|
||||
public void invokeCallback(final int callbackID, final NativeArray arguments) {
|
||||
public void invokeCallback(ExecutorToken executorToken, int callbackID, NativeArray arguments) {
|
||||
synchronized (mTeardownLock) {
|
||||
if (mDestroyed) {
|
||||
FLog.w(ReactConstants.TAG, "Invoking JS callback after bridge has been destroyed.");
|
||||
@@ -194,7 +197,7 @@ public class CatalystInstanceImpl implements CatalystInstance {
|
||||
|
||||
incrementPendingJSCalls();
|
||||
|
||||
Assertions.assertNotNull(mBridge).invokeCallback(callbackID, arguments);
|
||||
Assertions.assertNotNull(mBridge).invokeCallback(executorToken, callbackID, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +272,12 @@ public class CatalystInstanceImpl implements CatalystInstance {
|
||||
|
||||
@Override
|
||||
public <T extends JavaScriptModule> T getJSModule(Class<T> jsInterface) {
|
||||
return Assertions.assertNotNull(mJSModuleRegistry).getJavaScriptModule(jsInterface);
|
||||
return getJSModule(Assertions.assertNotNull(mMainExecutorToken), jsInterface);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends JavaScriptModule> T getJSModule(ExecutorToken executorToken, Class<T> jsInterface) {
|
||||
return Assertions.assertNotNull(mJSModuleRegistry).getJavaScriptModule(executorToken, jsInterface);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -388,7 +396,7 @@ public class CatalystInstanceImpl implements CatalystInstance {
|
||||
private class NativeModulesReactCallback implements ReactCallback {
|
||||
|
||||
@Override
|
||||
public void call(int moduleId, int methodId, ReadableNativeArray parameters) {
|
||||
public void call(ExecutorToken executorToken, int moduleId, int methodId, ReadableNativeArray parameters) {
|
||||
mReactQueueConfiguration.getNativeModulesQueueThread().assertIsOnThread();
|
||||
|
||||
// Suppress any callbacks if destroyed - will only lead to sadness.
|
||||
@@ -396,7 +404,7 @@ public class CatalystInstanceImpl implements CatalystInstance {
|
||||
return;
|
||||
}
|
||||
|
||||
mJavaRegistry.call(CatalystInstanceImpl.this, moduleId, methodId, parameters);
|
||||
mJavaRegistry.call(CatalystInstanceImpl.this, executorToken, moduleId, methodId, parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -441,12 +449,13 @@ public class CatalystInstanceImpl implements CatalystInstance {
|
||||
private class JSProfilerTraceListener implements TraceListener {
|
||||
@Override
|
||||
public void onTraceStarted() {
|
||||
getJSModule(com.facebook.react.bridge.Systrace.class).setEnabled(true);
|
||||
getJSModule(Assertions.assertNotNull(mMainExecutorToken), com.facebook.react.bridge.Systrace.class).setEnabled(
|
||||
true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTraceStopped() {
|
||||
getJSModule(com.facebook.react.bridge.Systrace.class).setEnabled(false);
|
||||
getJSModule(Assertions.assertNotNull(mMainExecutorToken), com.facebook.react.bridge.Systrace.class).setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.facebook.react.bridge;
|
||||
|
||||
import com.facebook.jni.HybridData;
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
|
||||
/**
|
||||
* Class corresponding to a JS VM that can call into native modules. In Java, this should
|
||||
* just be treated as a black box to be used to help the framework route native->JS calls back to
|
||||
* the proper JS VM. See {@link ReactContext#getJSModule(ExecutorToken, Class)} and
|
||||
* {@link BaseJavaModule#supportsWebWorkers()}.
|
||||
*
|
||||
* Note: If your application doesn't use web workers, it will only have a single ExecutorToken
|
||||
* per instance of React Native.
|
||||
*/
|
||||
@DoNotStrip
|
||||
public class ExecutorToken {
|
||||
|
||||
private final HybridData mHybridData;
|
||||
|
||||
@DoNotStrip
|
||||
private ExecutorToken(HybridData hybridData) {
|
||||
mHybridData = hybridData;
|
||||
}
|
||||
}
|
||||
@@ -12,12 +12,16 @@ package com.facebook.react.bridge;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.lang.Class;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.HashMap;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
|
||||
/**
|
||||
* Class responsible for holding all the {@link JavaScriptModule}s registered to this
|
||||
@@ -27,45 +31,71 @@ import com.facebook.infer.annotation.Assertions;
|
||||
*/
|
||||
/*package*/ class JavaScriptModuleRegistry {
|
||||
|
||||
private final HashMap<Class<? extends JavaScriptModule>, JavaScriptModule> mModuleInstances;
|
||||
private final CatalystInstanceImpl mCatalystInstance;
|
||||
private final WeakHashMap<ExecutorToken, HashMap<Class<? extends JavaScriptModule>, JavaScriptModule>> mModuleInstances;
|
||||
private final HashMap<Class<? extends JavaScriptModule>, JavaScriptModuleRegistration> mModuleRegistrations;
|
||||
|
||||
public JavaScriptModuleRegistry(
|
||||
CatalystInstanceImpl instance,
|
||||
JavaScriptModulesConfig config) {
|
||||
mModuleInstances = new HashMap<>();
|
||||
mCatalystInstance = instance;
|
||||
mModuleInstances = new WeakHashMap<>();
|
||||
mModuleRegistrations = new HashMap<>();
|
||||
for (JavaScriptModuleRegistration registration : config.getModuleDefinitions()) {
|
||||
Class<? extends JavaScriptModule> moduleInterface = registration.getModuleInterface();
|
||||
JavaScriptModule interfaceProxy = (JavaScriptModule) Proxy.newProxyInstance(
|
||||
moduleInterface.getClassLoader(),
|
||||
new Class[]{moduleInterface},
|
||||
new JavaScriptModuleInvocationHandler(instance, registration));
|
||||
|
||||
mModuleInstances.put(moduleInterface, interfaceProxy);
|
||||
mModuleRegistrations.put(registration.getModuleInterface(), registration);
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends JavaScriptModule> T getJavaScriptModule(Class<T> moduleInterface) {
|
||||
return (T) Assertions.assertNotNull(
|
||||
mModuleInstances.get(moduleInterface),
|
||||
"JS module " + moduleInterface.getSimpleName() + " hasn't been registered!");
|
||||
public synchronized <T extends JavaScriptModule> T getJavaScriptModule(ExecutorToken executorToken, Class<T> moduleInterface) {
|
||||
HashMap<Class<? extends JavaScriptModule>, JavaScriptModule> instancesForContext =
|
||||
mModuleInstances.get(executorToken);
|
||||
if (instancesForContext == null) {
|
||||
instancesForContext = new HashMap<>();
|
||||
mModuleInstances.put(executorToken, instancesForContext);
|
||||
}
|
||||
|
||||
JavaScriptModule module = instancesForContext.get(moduleInterface);
|
||||
if (module != null) {
|
||||
return (T) module;
|
||||
}
|
||||
|
||||
JavaScriptModuleRegistration registration =
|
||||
Assertions.assertNotNull(
|
||||
mModuleRegistrations.get(moduleInterface),
|
||||
"JS module " + moduleInterface.getSimpleName() + " hasn't been registered!");
|
||||
JavaScriptModule interfaceProxy = (JavaScriptModule) Proxy.newProxyInstance(
|
||||
moduleInterface.getClassLoader(),
|
||||
new Class[]{moduleInterface},
|
||||
new JavaScriptModuleInvocationHandler(executorToken, mCatalystInstance, registration));
|
||||
instancesForContext.put(moduleInterface, interfaceProxy);
|
||||
return (T) interfaceProxy;
|
||||
}
|
||||
|
||||
private static class JavaScriptModuleInvocationHandler implements InvocationHandler {
|
||||
|
||||
private final WeakReference<ExecutorToken> mExecutorToken;
|
||||
private final CatalystInstanceImpl mCatalystInstance;
|
||||
private final JavaScriptModuleRegistration mModuleRegistration;
|
||||
|
||||
public JavaScriptModuleInvocationHandler(
|
||||
ExecutorToken executorToken,
|
||||
CatalystInstanceImpl catalystInstance,
|
||||
JavaScriptModuleRegistration moduleRegistration) {
|
||||
mExecutorToken = new WeakReference<>(executorToken);
|
||||
mCatalystInstance = catalystInstance;
|
||||
mModuleRegistration = moduleRegistration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
ExecutorToken executorToken = mExecutorToken.get();
|
||||
if (executorToken == null) {
|
||||
FLog.w(ReactConstants.TAG, "Dropping JS call, ExecutorToken went away...");
|
||||
return null;
|
||||
}
|
||||
String tracingName = mModuleRegistration.getTracingName(method);
|
||||
mCatalystInstance.callFunction(
|
||||
executorToken,
|
||||
mModuleRegistration.getModuleId(),
|
||||
mModuleRegistration.getMethodId(method),
|
||||
Arguments.fromJavaArgs(args),
|
||||
|
||||
@@ -23,7 +23,7 @@ import com.fasterxml.jackson.core.JsonGenerator;
|
||||
*/
|
||||
public interface NativeModule {
|
||||
interface NativeMethod {
|
||||
void invoke(CatalystInstance catalystInstance, ReadableNativeArray parameters);
|
||||
void invoke(CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray parameters);
|
||||
String getType();
|
||||
}
|
||||
|
||||
@@ -74,7 +74,25 @@ public interface NativeModule {
|
||||
void onCatalystInstanceDestroy();
|
||||
|
||||
/**
|
||||
* Whether this native module supports calls from web workers. Ignored for now.
|
||||
* In order to support web workers, a module must be aware that it can be invoked from multiple
|
||||
* different JS VMs. Supporting web workers means recognizing things like:
|
||||
*
|
||||
* 1) ids (e.g. timer ids, request ids, etc.) may only unique on a per-VM basis
|
||||
* 2) the module needs to make sure to enqueue callbacks and JS module calls to the correct VM
|
||||
*
|
||||
* In order to facilitate this, modules that support web workers will have all their @ReactMethod-
|
||||
* annotated methods passed a {@link ExecutorToken} as the first parameter before any arguments
|
||||
* from JS. This ExecutorToken internally maps to a specific JS VM and can be used by the
|
||||
* framework to route calls appropriately. In order to make JS module calls correctly, start using
|
||||
* the version of {@link ReactContext#getJSModule(ExecutorToken, Class)} that takes an
|
||||
* ExecutorToken. It will ensure that any calls you dispatch to the returned object will go to
|
||||
* the right VM. For Callbacks, you don't have to do anything special -- the framework
|
||||
* automatically tags them with the correct ExecutorToken when the are created.
|
||||
*
|
||||
* Note: even though calls can come from multiple JS VMs on multiple threads, calls to this module
|
||||
* will still only occur on a single thread.
|
||||
*
|
||||
* @return whether this module supports web workers.
|
||||
*/
|
||||
boolean supportsWebWorkers();
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ public class NativeModuleRegistry {
|
||||
|
||||
/* package */ void call(
|
||||
CatalystInstance catalystInstance,
|
||||
ExecutorToken executorToken,
|
||||
int moduleId,
|
||||
int methodId,
|
||||
ReadableNativeArray parameters) {
|
||||
@@ -55,7 +56,7 @@ public class NativeModuleRegistry {
|
||||
if (definition == null) {
|
||||
throw new RuntimeException("Call to unknown module: " + moduleId);
|
||||
}
|
||||
definition.call(catalystInstance, methodId, parameters);
|
||||
definition.call(catalystInstance, executorToken, methodId, parameters);
|
||||
}
|
||||
|
||||
/* package */ void writeModuleDescriptions(JsonGenerator jg) throws IOException {
|
||||
@@ -164,12 +165,13 @@ public class NativeModuleRegistry {
|
||||
|
||||
public void call(
|
||||
CatalystInstance catalystInstance,
|
||||
ExecutorToken executorToken,
|
||||
int methodId,
|
||||
ReadableNativeArray parameters) {
|
||||
MethodRegistration method = this.methods.get(methodId);
|
||||
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, method.tracingName);
|
||||
try {
|
||||
this.methods.get(methodId).method.invoke(catalystInstance, parameters);
|
||||
this.methods.get(methodId).method.invoke(catalystInstance, executorToken, parameters);
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
}
|
||||
|
||||
@@ -79,12 +79,13 @@ public class ReactBridge extends Countable {
|
||||
*/
|
||||
public native void loadScriptFromAssets(AssetManager assetManager, String assetName);
|
||||
public native void loadScriptFromFile(@Nullable String fileName, @Nullable String sourceURL);
|
||||
public native void callFunction(int moduleId, int methodId, NativeArray arguments, String tracingName);
|
||||
public native void invokeCallback(int callbackID, NativeArray arguments);
|
||||
public native void callFunction(ExecutorToken executorToken, int moduleId, int methodId, NativeArray arguments, String tracingName);
|
||||
public native void invokeCallback(ExecutorToken executorToken, int callbackID, NativeArray arguments);
|
||||
public native void setGlobalVariable(String propertyName, String jsonEncodedArgument);
|
||||
public native boolean supportsProfiling();
|
||||
public native void startProfiler(String title);
|
||||
public native void stopProfiler(String title, String filename);
|
||||
public native ExecutorToken getMainExecutorToken();
|
||||
private native void handleMemoryPressureModerate();
|
||||
private native void handleMemoryPressureCritical();
|
||||
public native void destroy();
|
||||
|
||||
@@ -15,7 +15,7 @@ import com.facebook.proguard.annotations.DoNotStrip;
|
||||
public interface ReactCallback {
|
||||
|
||||
@DoNotStrip
|
||||
void call(int moduleId, int methodId, ReadableNativeArray parameters);
|
||||
void call(ExecutorToken executorToken, int moduleId, int methodId, ReadableNativeArray parameters);
|
||||
|
||||
@DoNotStrip
|
||||
void onBatchComplete();
|
||||
|
||||
@@ -96,6 +96,13 @@ public class ReactContext extends ContextWrapper {
|
||||
return mCatalystInstance.getJSModule(jsInterface);
|
||||
}
|
||||
|
||||
public <T extends JavaScriptModule> T getJSModule(ExecutorToken executorToken, Class<T> jsInterface) {
|
||||
if (mCatalystInstance == null) {
|
||||
throw new RuntimeException("Trying to invoke JS before CatalystInstance has been set!");
|
||||
}
|
||||
return mCatalystInstance.getJSModule(executorToken, jsInterface);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the instance of the specified module interface associated with this ReactContext.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user