本篇文章已受權微信公衆號 JueCode 獨家發佈javascript
小夥伴們都知道在Android開發中實現Java和JS的通訊能夠經過WebView來實現,包括註冊JSBridge或者在接口中攔截均可以。然而React Native中並無用WebView控件的方式,而是基於WebKit內核的方式來實現Java與JS的通訊,也就是Java與JS之間的通訊都是經過中間層C++來實現的,今天咱們來分析下Android中React Native怎麼實現Java和JS之間的通訊。java
React Native中調用全部的行爲都是從Native端發起的,用戶操做直接面向的也是Native。因此這個通訊模型又能夠當作是Native發起會話,而後Javascript進行應答。react
關於JavaScript調用Java,能夠參考個人另一篇博客:React Native通訊原理源碼分析二android
Java要能調用到JS須要在Java層註冊JS模塊,先來看下JS模塊的註冊過程。web
系統註冊了一些經常使用的JS模塊,在CoreModulesPackage的createJSModules中註冊了一些系統核心的JS模塊,好比AppRegistry(RN組件註冊模塊)/RCTEventEmitter(事件發射模塊)等。json
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層的實現。bash
全部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方法中去看下怎麼註冊的。app
全部的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++層,接下來進入這一層看看。
通訊模型圖中要調用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
與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
我的在簡書博客: juexingzhe