小伙伴们都知道在Android开发中实现Java和JS的通信可以通过WebView来实现,包括注册JSBridge或者在接口中拦截都可以。然而React Native中并没有用WebView控件的方式,而是基于WebKit内核的方式来实现Java与JS的通信,也就是Java与JS之间的通信都是通过中间层C++来实现的,今天我们来分析下Android中React Native怎么实现Java和JS之间的通信。
React Native中调用所有的行为都是从Native端发起的,用户操作直接面向的也是Native。所以这个通信模型又可以看成是Native发起会话,然后Javascript进行应答。
Java要能调用到JS需要在Java层注册JS模块,先来看下JS模块的注册过程。
1.JavaScriptModule模块注册(Java层)
系统注册了一些常用的JS模块,在CoreModulesPackage的createJSModules中注册了一些系统核心的JS模块,比如AppRegistry(RN组件注册模块)/RCTEventEmitter(事件发射模块)等。
class CoreModulesPackage implements ReactPackage{
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext catalystApplicationContext) {
......
return Arrays.<NativeModule>asList(
new AnimationsDebugModule(
catalystApplicationContext,
mReactInstanceManager.getDevSupportManager().getDevSettings()),
new AndroidInfoModule(),
new DeviceEventManagerModule(catalystApplicationContext, mHardwareBackBtnHandler),
new ExceptionsManagerModule(mReactInstanceManager.getDevSupportManager()),
new Timing(catalystApplicationContext),
new SourceCodeModule(
mReactInstanceManager.getSourceUrl(),
mReactInstanceManager.getDevSupportManager().getSourceMapUrl()),
uiManagerModule,
new DebugComponentOwnershipModule(catalystApplicationContext));
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Arrays.asList(
DeviceEventManagerModule.RCTDeviceEventEmitter.class,
JSTimersExecution.class,
RCTEventEmitter.class,
RCTNativeAppEventEmitter.class,
AppRegistry.class,
com.facebook.react.bridge.Systrace.class,
DebugComponentOwnershipModule.RCTDebugComponentOwnership.class);
}
}
public interface ReactPackage {
/**
* @param reactContext react application context that can be used to create modules
* @return list of native modules to register with the newly created catalyst instance
*/
List<NativeModule> createNativeModules(ReactApplicationContext reactContext);
/**
* @return list of JS modules to register with the newly created catalyst instance.
*
* IMPORTANT: Note that only modules that needs to be accessible from the native code should be
* listed here. Also listing a native module here doesn't imply that the JS implementation of it
* will be automatically included in the JS bundle.
*/
List<Class<? extends JavaScriptModule>> createJSModules();
/**
* @return a list of view managers that should be registered with {@link UIManagerModule}
*/
List<ViewManager> createViewManagers(ReactApplicationContext reactContext);
}
以RCTEventEmitter这个JavaScriptModule模块为例来看下JS模块在Java层的实现。
所有JS层组件实现JavaScriptModule接口,比如RCTEventEmitter:
public interface RCTEventEmitter extends JavaScriptModule {
public void receiveEvent(int targetTag, String eventName, @Nullable WritableMap event);
public void receiveTouches(
String eventName,
WritableArray touches,
WritableArray changedIndices);
}
可以看出来JS模块在Java层只是简单的继承JavaScriptModule,并没有具体的实现,那么问题就来了,光是一个接口是没法调用的,好像到这里就跟踪不下去了?其实可以换种思路,JS模块肯定是要在Native中注册的,我们先到ReactInstanceManagerImpl中的createReactContext方法中去看下怎么注册的。
所有的NativeModule和JSModule在ReactInstanceManagerImpl中注册
方法的代码比较多,但是逻辑不复杂,关键的几个步骤我都在代码中直接做了注释,就不多说了,这里主要分析JS模块的注册,所以我们看到JavaScriptModulesConfig中。
private ReactApplicationContext createReactContext(
JavaScriptExecutor jsExecutor,
JSBundleLoader jsBundleLoader) {
FLog.i(ReactConstants.TAG, "Creating react context.");
//Native模块
NativeModuleRegistry.Builder nativeRegistryBuilder = new NativeModuleRegistry.Builder();
//JS模块
JavaScriptModulesConfig.Builder jsModulesBuilder = new JavaScriptModulesConfig.Builder();
//React Context
ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
if (mUseDeveloperSupport) {
reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);
}
//处理CoreModules---包含Native模块和JS模块
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);
}
// 处理用户注册的模块
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);
}
}
//buildNativeModuleRegistry
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "buildNativeModuleRegistry");
NativeModuleRegistry nativeModuleRegistry;
try {
nativeModuleRegistry = nativeRegistryBuilder.build();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
//buildJSModuleConfig
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "buildJSModuleConfig");
JavaScriptModulesConfig javaScriptModulesConfig;
try {
javaScriptModulesConfig = jsModulesBuilder.build();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
...
//createCatalystInstance---Java/JS/C++三方通信总管
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
.setCatalystQueueConfigurationSpec(CatalystQueueConfigurationSpec.createDefault())
.setJSExecutor(jsExecutor)
.setRegistry(nativeModuleRegistry)
.setJSModulesConfig(javaScriptModulesConfig)
.setJSBundleLoader(jsBundleLoader)
.setNativeModuleCallExceptionHandler(exceptionHandler);
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance");
CatalystInstance catalystInstance;
try {
catalystInstance = catalystInstanceBuilder.build();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
if (mBridgeIdleDebugListener != null) {
catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
}
reactContext.initializeWithInstance(catalystInstance);
//runJSBundle
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "runJSBundle");
try {
catalystInstance.runJSBundle();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
ReactMarker.logMarker("CREATE_REACT_CONTEXT_END");
return reactContext;
}
看到JavaScriptModulesConfig,主要是对每个JavaScriptModule构造JavaScriptModuleRegistration,存放在mModules中
/**
* Class stores configuration of javascript modules that can be used across the bridge
*/
public class JavaScriptModulesConfig {
private final List<JavaScriptModuleRegistration> mModules;
private JavaScriptModulesConfig(List<JavaScriptModuleRegistration> modules) {
mModules = modules;
}
/*package*/ List<JavaScriptModuleRegistration> getModuleDefinitions() {
return mModules;
}
/*package*/ void writeModuleDescriptions(JsonGenerator jg) throws IOException {
jg.writeStartObject();
for (JavaScriptModuleRegistration registration : mModules) {
jg.writeObjectFieldStart(registration.getName());
appendJSModuleToJSONObject(jg, registration);
jg.writeEndObject();
}
jg.writeEndObject();
}
private void appendJSModuleToJSONObject(
JsonGenerator jg,
JavaScriptModuleRegistration registration) throws IOException {
jg.writeObjectField("moduleID", registration.getModuleId());
jg.writeObjectFieldStart("methods");
for (Method method : registration.getMethods()) {
jg.writeObjectFieldStart(method.getName());
jg.writeObjectField("methodID", registration.getMethodId(method));
jg.writeEndObject();
}
jg.writeEndObject();
}
public static class Builder {
private int mLastJSModuleId = 0;
private List<JavaScriptModuleRegistration> mModules =
new ArrayList<JavaScriptModuleRegistration>();
public Builder add(Class<? extends JavaScriptModule> moduleInterfaceClass) {
int moduleId = mLastJSModuleId++;
mModules.add(new JavaScriptModuleRegistration(moduleId, moduleInterfaceClass));
return this;
}
public JavaScriptModulesConfig build() {
return new JavaScriptModulesConfig(mModules);
}
}
}
/**
* Registration info for a {@link JavaScriptModule}.
Maps its methods to method ids.
*/
class JavaScriptModuleRegistration {
private final int mModuleId;
private final Class<? extends JavaScriptModule> mModuleInterface;
private final Map<Method, Integer> mMethodsToIds;
private final Map<Method, String> mMethodsToTracingNames;
JavaScriptModuleRegistration(int moduleId, Class<? extends JavaScriptModule> moduleInterface) {
mModuleId = moduleId;
mModuleInterface = moduleInterface;
mMethodsToIds = MapBuilder.newHashMap();
mMethodsToTracingNames = MapBuilder.newHashMap();
final Method[] declaredMethods = mModuleInterface.getDeclaredMethods();
Arrays.sort(declaredMethods, new Comparator<Method>() {
@Override
public int compare(Method lhs, Method rhs) {
return lhs.getName().compareTo(rhs.getName());
}
});
// Methods are sorted by name so we can dupe check and have obvious ordering
String previousName = null;
for (int i = 0; i < declaredMethods.length; i++) {
Method method = declaredMethods[i];
String name = method.getName();
Assertions.assertCondition(
!name.equals(previousName),
"Method overloading is unsupported: " + mModuleInterface.getName() + "#" + name);
previousName = name;
mMethodsToIds.put(method, i);
mMethodsToTracingNames.put(method, "JSCall__" + getName() + "_" + method.getName());
}
}
......
}
到这里就有了所有的JavaScriptModules,并且都扫描存放在JavaScriptModulesConfig中,那么RN框架是怎么使用的?再回顾前面ReactInstanceManagerImpl,将JavaScriptModulesConfig用来实例化catalystInstance,这个是三方通信的中转站。
//createCatalystInstance---Java/JS/C++三方通信总管
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
.setCatalystQueueConfigurationSpec(CatalystQueueConfigurationSpec.createDefault())
.setJSExecutor(jsExecutor)
.setRegistry(nativeModuleRegistry)
.setJSModulesConfig(javaScriptModulesConfig)
.setJSBundleLoader(jsBundleLoader)
.setNativeModuleCallExceptionHandler(exceptionHandler);
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance");
CatalystInstance catalystInstance;
try {
catalystInstance = catalystInstanceBuilder.build();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
catalystInstance是一个接口,实现类是CatalystInstanceImpl,跟到构造函数中:
private CatalystInstanceImpl(
final CatalystQueueConfigurationSpec catalystQueueConfigurationSpec,
final JavaScriptExecutor jsExecutor,
final NativeModuleRegistry registry,
final JavaScriptModulesConfig jsModulesConfig,
final JSBundleLoader jsBundleLoader,
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
...
mJSModuleRegistry = new JavaScriptModuleRegistry(CatalystInstanceImpl.this, jsModulesConfig);
...
}
通过JavaScriptModulesConfig实例化JavaScriptModuleRegistry,前面分析过JavaScriptModule在Java层都是implements JavaScriptModule的接口,那么怎么去调用方法?这里就是对每个接口通过动态代理实例化一个代理对象,Java调用JS方法时统一分发到JavaScriptModuleInvocationHandler中的invoke进行处理,在invoke中其实就是调用mCatalystInstance.callFunction
很明显,接下来需要到JavaScriptModuleRegistry中看看:
/**
* Class responsible for holding all the {@link JavaScriptModule}s registered to this
* {@link CatalystInstance}.
Uses Java proxy objects to dispatch method calls on JavaScriptModules
* to the bridge using the corresponding module and method ids so the proper function is executed in
* JavaScript.
*/
/*package*/ class JavaScriptModuleRegistry {
private final HashMap<Class<? extends JavaScriptModule>, JavaScriptModule> mModuleInstances;
public JavaScriptModuleRegistry(
CatalystInstanceImpl instance,
JavaScriptModulesConfig config) {
mModuleInstances = 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);
}
}
public <T extends JavaScriptModule> T getJavaScriptModule(Class<T> moduleInterface) {
return (T) Assertions.assertNotNull(
mModuleInstances.get(moduleInterface),
"JS module " + moduleInterface.getSimpleName() + " hasn't been registered!");
}
private static class JavaScriptModuleInvocationHandler implements InvocationHandler {
private final CatalystInstanceImpl mCatalystInstance;
private final JavaScriptModuleRegistration mModuleRegistration;
public JavaScriptModuleInvocationHandler(
CatalystInstanceImpl catalystInstance,
JavaScriptModuleRegistration moduleRegistration) {
mCatalystInstance = catalystInstance;
mModuleRegistration = moduleRegistration;
}
@Override
public @Nullable Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String tracingName = mModuleRegistration.getTracingName(method);
mCatalystInstance.callFunction(
mModuleRegistration.getModuleId(),
mModuleRegistration.getMethodId(method),
Arguments.fromJavaArgs(args),
tracingName);
return null;
}
}
}
在Java调用JS模块的方法时,都会通过代理对象调用invoke方法,在invoke方法中调用CatalystInstance.callFunction,CatalystInstance是一个接口,实现类CatalystInstanceImpl,看到CatalystInstanceImpl中的callFunction:
/* package */ void callFunction(
final int moduleId,
final int methodId,
final NativeArray arguments,
final String tracingName) {
if (mDestroyed) {
FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed.");
return;
}
incrementPendingJSCalls();
final int traceID = mTraceID++;
Systrace.startAsyncFlow(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
tracingName,
traceID);
mCatalystQueueConfiguration.getJSQueueThread().runOnQueue(
new Runnable() {
@Override
public void run() {
mCatalystQueueConfiguration.getJSQueueThread().assertIsOnThread();
Systrace.endAsyncFlow(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
tracingName,
traceID);
if (mDestroyed) {
return;
}
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, tracingName);
try {
Assertions.assertNotNull(mBridge).callFunction(moduleId, methodId, arguments);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
});
}
所有Java层向Javascript层的通信请求都是走的ReactBridge.callFunction
其实就是在JSQueueThread线程中调用ReactBridge的callFunction,是Native函数,到这里就从Java转到C++层了
/**
* Interface to the JS execution environment and means of transport for messages Java<->JS.
*/
@DoNotStrip
public class ReactBridge extends Countable {
/* package */ static final String REACT_NATIVE_LIB = "reactnativejni";
static {
SoLoader.loadLibrary(REACT_NATIVE_LIB);
}
private final ReactCallback mCallback;
private final JavaScriptExecutor mJSExecutor;
private final MessageQueueThread mNativeModulesQueueThread;
/**
* @param jsExecutor the JS executor to use to run JS
* @param callback the callback class used to invoke native modules
* @param nativeModulesQueueThread the MessageQueueThread the callbacks should be invoked on
*/
public ReactBridge(
JavaScriptExecutor jsExecutor,
ReactCallback callback,
MessageQueueThread nativeModulesQueueThread) {
mJSExecutor = jsExecutor;
mCallback = callback;
mNativeModulesQueueThread = nativeModulesQueueThread;
initialize(jsExecutor, callback, mNativeModulesQueueThread);
}
@Override
public void dispose() {
mJSExecutor.close();
mJSExecutor.dispose();
super.dispose();
}
public void handleMemoryPressure(MemoryPressure level) {
switch (level) {
case MODERATE:
handleMemoryPressureModerate();
break;
case CRITICAL:
handleMemoryPressureCritical();
break;
default:
throw new IllegalArgumentException("Unknown level: " + level);
}
}
private native void initialize(
JavaScriptExecutor jsExecutor,
ReactCallback callback,
MessageQueueThread nativeModulesQueueThread);
/**
* All native functions are not thread safe and appropriate queues should be used
*/
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);
public native void invokeCallback(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);
private native void handleMemoryPressureModerate();
private native void handleMemoryPressureCritical();
}
再回头看看ReactBridge在CatalystInstanceImpl里面的初始化,初始化会ReactBridge调用setGlobalVariable,这是个Native函数,是在C++层注册用的,先按下后面再分析组件调用过程再分析,先看看 buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig)做了什么。
private CatalystInstanceImpl(
final CatalystQueueConfigurationSpec catalystQueueConfigurationSpec,
final JavaScriptExecutor jsExecutor,
final NativeModuleRegistry registry,
final JavaScriptModulesConfig jsModulesConfig,
final JSBundleLoader jsBundleLoader,
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
......
try {
mBridge = mCatalystQueueConfiguration.getJSQueueThread().callOnQueue(
new Callable<ReactBridge>() {
@Override
public ReactBridge call() throws Exception {
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "initializeBridge");
try {
return initializeBridge(jsExecutor, jsModulesConfig);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
}).get(BRIDGE_SETUP_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (Exception t) {
throw new RuntimeException("Failed to initialize bridge", t);
}
}
private ReactBridge initializeBridge(
JavaScriptExecutor jsExecutor,
JavaScriptModulesConfig jsModulesConfig) {
mCatalystQueueConfiguration.getJSQueueThread().assertIsOnThread();
Assertions.assertCondition(mBridge == null, "initializeBridge should be called once");
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "ReactBridgeCtor");
ReactBridge bridge;
try {
bridge = new ReactBridge(
jsExecutor,
new NativeModulesReactCallback(),
mCatalystQueueConfiguration.getNativeModulesQueueThread());
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "setBatchedBridgeConfig");
try {
bridge.setGlobalVariable(
"__fbBatchedBridgeConfig",
buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig));
bridge.setGlobalVariable(
"__RCTProfileIsProfiling",
Systrace.isTracing(Systrace.TRACE_TAG_REACT_APPS) ? "true" : "false");
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
return bridge;
}
在CatalystInstanceImpl中的实现,其实就是写一个JSON字符串,有两个字段"remoteModuleConfig"和"localModulesConfig",分别对应NativeModule(Native提供给JS调用)和JSModule(JS提供给Native调用)
private String buildModulesConfigJSONProperty(
NativeModuleRegistry nativeModuleRegistry,
JavaScriptModulesConfig jsModulesConfig) {
JsonFactory jsonFactory = new JsonFactory();
StringWriter writer = new StringWriter();
try {
JsonGenerator jg = jsonFactory.createGenerator(writer);
jg.writeStartObject();
jg.writeFieldName("remoteModuleConfig");
nativeModuleRegistry.writeModuleDescriptions(jg);
jg.writeFieldName("localModulesConfig");
jsModulesConfig.writeModuleDescriptions(jg);
jg.writeEndObject();
jg.close();
} catch (IOException ioe) {
throw new RuntimeException("Unable to serialize JavaScript module declaration", ioe);
}
return writer.getBuffer().toString();
}
再看看localModulesConfig都写入了什么,跟进去JavaScriptModulesConfig.java,就是往JSON字符创中先写入接口名、moduleID、methods(方法名、methodID)等。
/*package*/ void writeModuleDescriptions(JsonGenerator jg) throws IOException {
jg.writeStartObject();
for (JavaScriptModuleRegistration registration : mModules) {
jg.writeObjectFieldStart(registration.getName());
appendJSModuleToJSONObject(jg, registration);
jg.writeEndObject();
}
jg.writeEndObject();
}
private void appendJSModuleToJSONObject(
JsonGenerator jg,
JavaScriptModuleRegistration registration) throws IOException {
jg.writeObjectField("moduleID", registration.getModuleId());
jg.writeObjectFieldStart("methods");
for (Method method : registration.getMethods()) {
jg.writeObjectFieldStart(method.getName());
jg.writeObjectField("methodID", registration.getMethodId(method));
jg.writeEndObject();
}
jg.writeEndObject();
}
从initializeBridge->setGlobalVariable->buildModulesConfigJSONProperty->writeModuleDescriptions
整个过程作用是将所有JavaScriptModule的信息生成JSON字符串预先保存到Bridge中, 通过methodID标识作为key存入JSON生成器中,用来最终生成JSON字符串:
其中setGlobalVariable也是Native函数,buildModulesConfigJSONProperty把所有的JavaScriptModule模块以moduleID+methodID形式生成JSON字符串,通过setGlobalVariable把JSON字符串预先存入了ReactBridge中
bridge.setGlobalVariable(
"__fbBatchedBridgeConfig",
buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig));
Java层的调用就是上面的过程,最后通过JNI调用到C++层,接下来进入这一层看看。
2.ReactBridge实现
通信模型图中要调用WebKit的实现,少不了Bridge这个桥梁,因为Java是不能直接调用WebKit,但是可以Java通过JNI,JNI再调用WebKit
JNI入口react/jni/OnLoad.cpp,通过RegisterNatives方式注册的,JNI_OnLoad里面注册了setGlobalVariable和callFunction等native本地方法
//jni/react/jni/OnLoad.cpp
namespace bridge {
......
static void setGlobalVariable(JNIEnv* env, jobject obj, jstring propName, jstring jsonValue) {
auto bridge = extractRefPtr<Bridge>(env, obj);
bridge->setGlobalVariable(fromJString(env, propName), fromJString(env, jsonValue));
}
...
} // namespace bridge
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
return initialize(vm, [] {
// get the current env
JNIEnv* env = Environment::current();
registerNatives("com/facebook/react/bridge/ReadableNativeMap$ReadableNativeMapKeySetIterator", {
......
registerNatives("com/facebook/react/bridge/ReactBridge", {
makeNativeMethod("initialize", "(Lcom/facebook/react/bridge/JavaScriptExecutor;Lcom/facebook/react/bridge/ReactCallback;Lcom/facebook/react/bridge/queue/MessageQueueThread;)V", bridge::create),
makeNativeMethod(
"loadScriptFromAssets", "(Landroid/content/res/AssetManager;Ljava/lang/String;)V",
bridge::loadScriptFromAssets),
makeNativeMethod("loadScriptFromFile", bridge::loadScriptFromFile),
makeNativeMethod("callFunction", bridge::callFunction),
makeNativeMethod("invokeCallback", bridge::invokeCallback),
makeNativeMethod("setGlobalVariable", bridge::setGlobalVariable),
makeNativeMethod("supportsProfiling", bridge::supportsProfiling),
makeNativeMethod("startProfiler", bridge::startProfiler),
makeNativeMethod("stopProfiler", bridge::stopProfiler),
makeNativeMethod("handleMemoryPressureModerate", bridge::handleMemoryPressureModerate),
makeNativeMethod("handleMemoryPressureCritical", bridge::handleMemoryPressureCritical),
});
}
可以看到setGlobalVariable真正调用的是Bridge.cpp里面的setGlobalVariable:
//jni/react/Bridge.h
class JSThreadState;
class Bridge : public Countable {
public:
typedef std::function<void(std::vector<MethodCall>, bool isEndOfBatch)> Callback;
Bridge(const RefPtr<JSExecutorFactory>& jsExecutorFactory, Callback callback);
virtual ~Bridge();
/**
* Flush get the next queue of changes.
*/
void flush();
/**
* Executes a function with the module ID and method ID and any additional
* arguments in JS.
*/
void callFunction(const double moduleId, const double methodId, const folly::dynamic& args);
/**
* Invokes a callback with the cbID, and optional additional arguments in JS.
*/
void invokeCallback(const double callbackId, const folly::dynamic& args);
void executeApplicationScript(const std::string& script, const std::string& sourceURL);
void setGlobalVariable(const std::string& propName, const std::string& jsonValue);
bool supportsProfiling();
void startProfiler(const std::string& title);
void stopProfiler(const std::string& title, const std::string& filename);
void handleMemoryPressureModerate();
void handleMemoryPressureCritical();
private:
Callback m_callback;
std::unique_ptr<JSThreadState> m_threadState;
// This is used to avoid a race condition where a proxyCallback gets queued after ~Bridge(),
// on the same thread. In that case, the callback will try to run the task on m_callback which
// will have been destroyed within ~Bridge(), thus causing a SIGSEGV.
std::shared_ptr<bool> m_destroyed;
};
void Bridge::setGlobalVariable(const std::string& propName, const std::string& jsonValue) {
m_threadState->setGlobalVariable(propName, jsonValue);
}
最终调用的是JSThreadState里面的setGlobalVariable:
//jni/react/Bridge.cpp
class JSThreadState {
public:
JSThreadState(const RefPtr<JSExecutorFactory>& jsExecutorFactory, Bridge::Callback&& callback) :
m_callback(callback)
{
m_jsExecutor = jsExecutorFactory->createJSExecutor([this, callback] (std::string queueJSON, bool isEndOfBatch) {
m_callback(parseMethodCalls(queueJSON), false /* = isEndOfBatch */);
});
}
void setGlobalVariable(const std::string& propName, const std::string& jsonValue) {
m_jsExecutor->setGlobalVariable(propName, jsonValue);
}
private:
std::unique_ptr<JSExecutor> m_jsExecutor;
Bridge::Callback m_callback;
};
最终调用JSExecutor里面setGlobalVariable:
//jni/react/JSCExecutor.h
class JSCExecutor : public JSExecutor, public JSCWebWorkerOwner {
public:
/**
* Should be invoked from the JS thread.
*/
explicit JSCExecutor(FlushImmediateCallback flushImmediateCallback);
~JSCExecutor() override;
virtual void executeApplicationScript(
const std::string& script,
const std::string& sourceURL) override;
virtual std::string flush() override;
virtual std::string callFunction(
const double moduleId,
const double methodId,
const folly::dynamic& arguments) override;
virtual std::string invokeCallback(
const double callbackId,
const folly::dynamic& arguments) override;
virtual void setGlobalVariable(
const std::string& propName,
const std::string& jsonValue) override;
virtual bool supportsProfiling() override;
virtual void startProfiler(const std::string &titleString) override;
virtual void stopProfiler(const std::string &titleString, const std::string &filename) override;
virtual void handleMemoryPressureModerate() override;
virtual void handleMemoryPressureCritical() override;
void flushQueueImmediate(std::string queueJSON);
void installNativeHook(const char *name, JSObjectCallAsFunctionCallback callback);
virtual void onMessageReceived(int workerId, const std::string& message) override;
virtual JSGlobalContextRef getContext() override;
virtual std::shared_ptr<JMessageQueueThread> getMessageQueueThread() override;
};
} }
//jni/react/JSCExecutor.cpp
void JSCExecutor::setGlobalVariable(const std::string& propName, const std::string& jsonValue) {
auto globalObject = JSContextGetGlobalObject(m_context);
String jsPropertyName(propName.c_str());
String jsValueJSON(jsonValue.c_str());
auto valueToInject = JSValueMakeFromJSONString(m_context, jsValueJSON);
JSObjectSetProperty(m_context, globalObject, jsPropertyName, valueToInject, 0, NULL);
}
从JSCExecutor::setGlobalVariable中可以看到java层传过来的JSON串(包含Native Modules和JS Modules)和属性名(__fbBatchedBridgeConfig),赋值给全局变量globalObject,这个全局变量通过JSContextGetGlobalObject获取到,在JSCExecutor.h头文件中:
#include <memory>
#include <unordered_map>
#include <JavaScriptCore/JSContextRef.h>
#include "Executor.h"
#include "JSCHelpers.h"
#include "JSCWebWorker.h"
JavaScriptCore/JSContextRef.h是Webkit这个库中的文件,在ReactAndroid下的build.gradle中有索引这个库,到build.gradle中看看:
//ReactAndroid/build.gradle
task downloadJSCHeaders(type: Download) {
def jscAPIBaseURL = 'https://svn.webkit.org/repository/webkit/!svn/bc/174650/trunk/Source/JavaScriptCore/API/'
def jscHeaderFiles = ['JSBase.h', 'JSContextRef.h', 'JSObjectRef.h', 'JSRetainPtr.h', 'JSStringRef.h', 'JSValueRef.h', 'WebKitAvailability.h']
def output = new File(downloadsDir, 'jsc')
output.mkdirs()
src(jscHeaderFiles.collect { headerName -> "$jscAPIBaseURL$headerName" })
onlyIfNewer true
overwrite false
dest output
}
打开jscAPIBaseURL这个url可以找到JSContextRef.h头文件:
//JavaScriptCore/API/JSContextRef.h
...
/*!
@function
@abstract Gets the global object of a JavaScript execution context.
@param ctx The JSContext whose global object you want to get.
@result ctx's global object.
*/
JS_EXPORT JSObjectRef JSContextGetGlobalObject(JSContextRef ctx);
...
JSCExecutor::setGlobalVariable最后给JS的全局变量设置一个属性__fbBatchedBridgeConfig
,这个属性的值就是JavaScriptModule,Java调用JS的时候从属性中查找,看下callFunction:
//jni/react/JSCExecutor.cpp
std::string JSCExecutor::callFunction(const double moduleId, const double methodId, const folly::dynamic& arguments) {
// TODO: Make this a first class function instead of evaling. #9317773
std::vector<folly::dynamic> call{
(double) moduleId,
(double) methodId,
std::move(arguments),
};
return executeJSCallWithJSC(m_context, "callFunctionReturnFlushedQueue", std::move(call));
}
static std::string executeJSCallWithJSC(
JSGlobalContextRef ctx,
const std::string& methodName,
const std::vector<folly::dynamic>& arguments) {
#ifdef WITH_FBSYSTRACE
FbSystraceSection s(
TRACE_TAG_REACT_CXX_BRIDGE, "JSCExecutor.executeJSCall",
"method", methodName);
#endif
// Evaluate script with JSC
folly::dynamic jsonArgs(arguments.begin(), arguments.end());
auto js = folly::to<folly::fbstring>(
"__fbBatchedBridge.", methodName, ".apply(null, ",
folly::toJson(jsonArgs), ")");
auto result = evaluateScript(ctx, String(js.c_str()), nullptr);
return Value(ctx, result).toJSONString();
}
最终以
__fbBatchedBridge.callFunctionReturnFlushedQueue.apply(null,{moduleId, methodId, arguments})
的形式拼接一个apply的Javascript执行语句,最后调用jni/react/JSCHelpers.cpp的evaluateScript的来执行这个语句,完成Bridge向Javascript的调用,JSCHelpers对WebKit的一些API做了封装,它负责最终调用WebKit:
//jni/react/JSCHelpers.h
namespace facebook {
namespace react {
void installGlobalFunction(
JSGlobalContextRef ctx,
const char* name,
JSObjectCallAsFunctionCallback callback);
JSValueRef makeJSCException(
JSContextRef ctx,
const char* exception_text);
JSValueRef evaluateScript(JSContextRef context, JSStringRef script, JSStringRef source);
} }
Bridge层的调用过程: OnLoad.cpp->Bridge.cpp->JSCExecutor.cpp->JSCHelpers.cpp->WebKit
3.JavaScript层实现
与Javascript的通信,实质上是Weikit执行Javascript语句,调用流程是Bridge->WebKit->Javascript,WebKit中提供了许多与Javascript通信的API,比如evaluateScript、JSContextGetGlobalObject、JSObjectSetProperty等。
回顾一下前面JSCExecutor::setGlobalVariable:
//jni/react/JSCExecutor.cpp
void JSCExecutor::setGlobalVariable(const std::string& propName, const std::string& jsonValue) {
auto globalObject = JSContextGetGlobalObject(m_context);
String jsPropertyName(propName.c_str());
String jsValueJSON(jsonValue.c_str());
auto valueToInject = JSValueMakeFromJSONString(m_context, jsValueJSON);
JSObjectSetProperty(m_context, globalObject, jsPropertyName, valueToInject, 0, NULL);
}
所有JavaScriptModule信息是调用的setGlobalVariable方法生成一张映射表,这张映射表最终肯定是要保存在Javascript层的,JSContextGetGlobalObject是WeiKit的方法,其目的是获取Global全局对象,jsPropertyName方法字面意思就是Javascript对象的属性名,参数propName是从Java层传递过来的,在CatalystInstanceImpl.java类中可以印证这一点,具体值有两个:__fbBatchedBridgeConfig和__RCTProfileIsProfiling.
//com/facebook/react/bridge/CatalystInstanceImpl
bridge.setGlobalVariable(
"__fbBatchedBridgeConfig",
buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig));
翻译成JS语句大概是:
global.__fbBatchedBridgeConfig = jsonValue;
由于__fbBatchedBridgeConfig对象是被直接定义成Global全局对象的属性,就可以直接调用了,类似于window对象,__fbBatchedBridgeConfig对象里又有两个属性:remoteModuleConfig和localModulesConfig,分别对应Java模块和JS模块。
到JS层的BatchedBridge.js:
//Libraries/BatchedBridge/BatchedBridge.js
const MessageQueue = require('MessageQueue');
const BatchedBridge = new MessageQueue(
__fbBatchedBridgeConfig.remoteModuleConfig, //Native(Java)模块
__fbBatchedBridgeConfig.localModulesConfig, //JS模块
);
// TODO: Move these around to solve the cycle in a cleaner way.
const Systrace = require('Systrace');
const JSTimersExecution = require('JSTimersExecution');
BatchedBridge.registerCallableModule('Systrace', Systrace);
BatchedBridge.registerCallableModule('JSTimersExecution', JSTimersExecution);
if (__DEV__) {
BatchedBridge.registerCallableModule('HMRClient', require('HMRClient'));
}
Object.defineProperty(global, '__fbBatchedBridge', { value: BatchedBridge });
module.exports = BatchedBridge;
1.在全局变量global中定义fbBatchedBridge,在前面JSCExecutor::callFunction中调用JS语句的形式是fbBatchedBridge.callFunctionReturnFlushedQueue.apply(null,{moduleId, methodId, arguments})
2.BatchedBridge=new MessageQueue
所以再继续跟到MessageQueue.js中:
class MessageQueue {
constructor(remoteModules, localModules) {
this.RemoteModules = {};
this._callableModules = {};
this._queue = [[], [], [], 0];
this._moduleTable = {};
this._methodTable = {};
this._callbacks = [];
this._callbackID = 0;
this._callID = 0;
this._lastFlush = 0;
this._eventLoopStartTime = new Date().getTime();
[
'invokeCallbackAndReturnFlushedQueue',
'callFunctionReturnFlushedQueue',
'flushedQueue',
].forEach((fn) => this[fn] = this[fn].bind(this));
let modulesConfig = this._genModulesConfig(remoteModules);
this._genModules(modulesConfig);
localModules && this._genLookupTables(
this._genModulesConfig(localModules),this._moduleTable, this._methodTable
);
this._debugInfo = {};
this._remoteModuleTable = {};
this._remoteMethodTable = {};
this._genLookupTables(
modulesConfig, this._remoteModuleTable, this._remoteMethodTable
);
}
/**
* Public APIs
*/
callFunctionReturnFlushedQueue(module, method, args) {
guard(() => {
this.__callFunction(module, method, args);
this.__callImmediates();
});
return this.flushedQueue();
}
_genLookupTables(modulesConfig, moduleTable, methodTable) {
modulesConfig.forEach((config, moduleID) => {
this._genLookup(config, moduleID, moduleTable, methodTable);
});
}
_genLookup(config, moduleID, moduleTable, methodTable) {
if (!config) {
return;
}
let moduleName, methods;
if (moduleHasConstants(config)) {
[moduleName, , methods] = config;
} else {
[moduleName, methods] = config;
}
moduleTable[moduleID] = moduleName;
methodTable[moduleID] = Object.assign({}, methods);
}
}
module.exports = MessageQueue;
把remoteModules, localModules传进了_genLookupTables的方法里,同时还有两个参数_moduleTable、_methodTable,就是我们找的映射表了,一张module映射表,一张method映射表
再回顾下前面Java调用JS的语句形式:__fbBatchedBridge.callFunctionReturnFlushedQueue.apply(null,{moduleId, methodId, arguments})
class MessageQueue {
constructor(remoteModules, localModules) {
this.RemoteModules = {};
this._callableModules = {};
this._queue = [[], [], [], 0];
this._moduleTable = {};
this._methodTable = {};
this._callbacks = [];
this._callbackID = 0;
this._callID = 0;
this._lastFlush = 0;
this._eventLoopStartTime = new Date().getTime();
[
'invokeCallbackAndReturnFlushedQueue',
'callFunctionReturnFlushedQueue',
'flushedQueue',
].forEach((fn) => this[fn] = this[fn].bind(this));
let modulesConfig = this._genModulesConfig(remoteModules);
this._genModules(modulesConfig);
localModules && this._genLookupTables(
this._genModulesConfig(localModules),this._moduleTable, this._methodTable
);
this._debugInfo = {};
this._remoteModuleTable = {};
this._remoteMethodTable = {};
this._genLookupTables(
modulesConfig, this._remoteModuleTable, this._remoteMethodTable
);
}
/**
* Public APIs
*/
callFunctionReturnFlushedQueue(module, method, args) {
guard(() => {
this.__callFunction(module, method, args);
this.__callImmediates();
});
return this.flushedQueue();
}
_genLookupTables(modulesConfig, moduleTable, methodTable) {
modulesConfig.forEach((config, moduleID) => {
this._genLookup(config, moduleID, moduleTable, methodTable);
});
}
_genLookup(config, moduleID, moduleTable, methodTable) {
if (!config) {
return;
}
let moduleName, methods;
if (moduleHasConstants(config)) {
[moduleName, , methods] = config;
} else {
[moduleName, methods] = config;
}
moduleTable[moduleID] = moduleName;
methodTable[moduleID] = Object.assign({}, methods);
}
}
module.exports = MessageQueue;
其中有个callableModules, 它就是用来存放哪些Javascript组件是可以被调用的,正常情况下callableModules的数据和JavaScriptModules的数据(包括方法名和参数)理应是完全对应的.
看下哪些JS组件调用registerCallableModule进行方法的注册看一下RCTEventEmitter.js:
//Libraries/BatchedBridge/BatchedBridgedModules/RCTEventEmitter.js
var BatchedBridge = require('BatchedBridge');
var ReactNativeEventEmitter = require('ReactNativeEventEmitter');
BatchedBridge.registerCallableModule(
'RCTEventEmitter',
ReactNativeEventEmitter
);
// Completely locally implemented - no native hooks.
module.exports = ReactNativeEventEmitter;
ReactNativeEventEmitter.js:
var ReactNativeEventEmitter = merge(ReactEventEmitterMixin, {
receiveEvent: function(
tag: number,
topLevelType: string,
nativeEventParam: Object
) {
......
},
receiveTouches: function(
eventTopLevelType: string,
touches: Array<Object>,
changedIndices: Array<number>
) {
......
});
对比下Java层的接口表示,方法名称和参数个数是一致的
public interface RCTEventEmitter extends JavaScriptModule {
public void receiveEvent(int targetTag, String eventName, @Nullable WritableMap event);
public void receiveTouches(
String eventName,
WritableArray touches,
WritableArray changedIndices);
}
继续前面__callFunction的最后一步:moduleMethods[method].apply(moduleMethods, args);调用对应模块的方法。
4.总结
Java调用JavaScript可以总结为下面:
欢迎关注公众号:JueCode