這篇文章針對于對rn有些基礎(chǔ)的同學(xué)盯串,沒有基礎(chǔ)的同學(xué)可以先了解一下rn以后再看這篇文章剔宪。要想深入理解 React Native 的工作原理歌粥,有兩個部分的源碼需要閱讀:rn的初始化和java與js通信的過程,首先分析rn的初始化過程院仿。
一、RN的初始化過程
從官方rn的demo入手速和,看rn是如何一步步如何初始化的:
public class MainActivity extends ReactActivity {
/**
* 用來返回要顯示的js端的組件的名稱意蛀,這個要和js端注冊的Component名稱一一對應(yīng)耸别。
*/
@Override
protected String getMainComponentName() {
return "AwesomeProject";
}
}
MainActivity繼承于ReactActivity,ReactActivity是rn中頁面顯示的入口县钥,負(fù)責(zé)頁面的顯示秀姐,下面就進(jìn)入源碼看它怎么實現(xiàn)的:
進(jìn)入ReactActivity的onCreate中發(fā)現(xiàn)ReactActivity只是一個空殼子,所有的邏輯都交給ReactActivityDelegate類實現(xiàn)若贮,這是典型的代理模式省有,這樣做的好處:1、實現(xiàn)和接口分開谴麦;2蠢沿、可以在FragmentActivity也同樣可以使用,不用維護(hù)兩套邏輯匾效。
接著查看ReactActivityDelegate的onCreate方法舷蟀,這個函數(shù)中最重要的邏輯就是loadApp方法:
protected void loadApp(String appKey) {
...
mReactRootView = createRootView();
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(),
appKey,
getLaunchOptions());
getPlainActivity().setContentView(mReactRootView);
}
這個函數(shù)主要實現(xiàn)兩個功能:
1、創(chuàng)建ReactRootView面哼,并將這個view設(shè)置為activity的根view野宜。ReactRootView繼承FrameLayout,它主要負(fù)責(zé)native端事件(鍵盤事件魔策、touch事件匈子、頁面大小變化等)的監(jiān)聽并將結(jié)果傳遞給js端以及負(fù)責(zé)頁面元素的重新繪制。
2闯袒、調(diào)用ReactRootView的startReactApplication方來虎敦,來啟動整個rn流。
startReactApplication函數(shù):
public void startReactApplication(
ReactInstanceManager reactInstanceManager,
String moduleName,
@Nullable Bundle launchOptions) {
...
//rn的上下文沒有創(chuàng)建
if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
//創(chuàng)建rn的上下文ReactContext對象
mReactInstanceManager.createReactContextInBackground();
}
if (mWasMeasured) {
attachToReactInstanceManager();
}
}
由于是第一次調(diào)用政敢,hasStartedCreatingInitialContext函數(shù)返回false其徙,此時,進(jìn)入mReactInstanceManager.createReactContextInBackground()函數(shù)喷户,在分析這個函數(shù)之前擂橘,先介紹一下兩個類:
ReactInstanceManager,rn的java端的控制器摩骨,它主要的功能是創(chuàng)建和管理CatalystInstance實例并和ReactActivity的生命周期保持一致。
CatalystInstance:jsc橋梁接口類朗若,為java和js相互通信提供環(huán)境恼五。
進(jìn)入createReactContextInBackground函數(shù),發(fā)現(xiàn)函數(shù)最后會走到recreateReactContextInBackgroundInner()函數(shù)哭懈。
recreateReactContextInBackgroundInner函數(shù):
private void recreateReactContextInBackgroundInner() {
...
//是否使用開發(fā)者模式灾馒,默認(rèn)情況下是true
if (mUseDeveloperSupport && mJSMainModuleName != null) {
final DeveloperSettings devSettings = mDevSupportManager.getDevSettings();
// 本地bundle文件是最新的并且不開啟遠(yuǎn)程js調(diào)試,用本地的bundle文件
if (mDevSupportManager.hasUpToDateJSBundleInCache() &&
!devSettings.isRemoteJSDebugEnabled()) {
onJSBundleLoadedFromServer();
} else if (mBundleLoader == null) {
mDevSupportManager.handleReloadJS();
} else {
//調(diào)用okHttp下載bundle文件
mDevSupportManager.isPackagerRunning(
new DevServerHelper.PackagerStatusCallback() {
@Override
public void onPackagerStatusFetched(final boolean packagerIsRunning) {
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
if (packagerIsRunning) {
mDevSupportManager.handleReloadJS();
} else {
devSettings.setRemoteJSDebugEnabled(false);
recreateReactContextInBackgroundFromBundleLoader();
}
}
});
}
});
}
return;
}
recreateReactContextInBackgroundFromBundleLoader();
}
recreateReactContextInBackgroundFromBundleLoader函數(shù):
private void recreateReactContextInBackgroundFromBundleLoader() {
recreateReactContextInBackground(
new JSCJavaScriptExecutor.Factory(mJSCConfig.getConfigMap()),
mBundleLoader);
}
JSCJavaScriptExecutor:是JavaScriptExecutor的子類遣总,是js執(zhí)行器睬罗。
JSCJavaScriptExecutor.Factory:工廠模式轨功,產(chǎn)生JSCJavaScriptExecutor實例。
public class JSCJavaScriptExecutor extends JavaScriptExecutor {
public static class Factory implements JavaScriptExecutor.Factory {
private ReadableNativeArray mJSCConfig;
public Factory(WritableNativeMap jscConfig) {
array.pushMap(jscConfig);
mJSCConfig = array;
}
//創(chuàng)建JSCJavaScriptExecutor實例
@Override
public JavaScriptExecutor create() throws Exception {
return new JSCJavaScriptExecutor(mJSCConfig);
}
}
public JSCJavaScriptExecutor(ReadableNativeArray jscConfig) {
super(initHybrid(jscConfig));
}
private native static HybridData initHybrid(ReadableNativeArray jscConfig);
}
JSCJavaScriptExecutor的構(gòu)造函數(shù)中調(diào)用initHybridn函數(shù)容达,這個函數(shù)在rn中反復(fù)的出現(xiàn)古涧,它的主要作用是找到于java相對應(yīng)的c++類并調(diào)用其構(gòu)造方法生成對象,把new出來對象的地址放到j(luò)ava的HybridData對象中花盐。其中與JSCJavaScriptExecutor對應(yīng)的C++類是JSCJavaScriptExecutorHolder(后面再介紹)羡滑。接著往下看,發(fā)現(xiàn)函數(shù)最后會調(diào)用ReactContextInitAsyncTask的doInBackground方法:
@Override
protected Result<ReactApplicationContext> doInBackground(ReactContextInitParams... params) {
...
try {
//創(chuàng)建JavaScriptExecutor實例
JavaScriptExecutor jsExecutor = params[0].getJsExecutorFactory().create();
return Result.of(createReactContext(jsExecutor, params[0].getJsBundleLoader()));
} catch (Exception e) {
return Result.of(e);
}
}
createReactContext函數(shù)比較長算芯,此處選擇比較重要的功能進(jìn)行分析柒昏,剩下的可以自自行閱讀,在分析代碼之前熙揍,先看幾個重要的類:
JSBundleLoader:bundle.js文件加載器职祷,在rn中有三種加載方式:1、加載本地文件届囚;2有梆、加載網(wǎng)絡(luò)文件,并將文件緩存奖亚;3淳梦、加載網(wǎng)絡(luò)文件,用于debug調(diào)試昔字。
ModuleSpec:NativeModule的包裝類爆袍,主要是為了實現(xiàn)module的懶加載,由于rn中native module比較多作郭,為了節(jié)省成本陨囊,rn中采用時懶加載的策略,只有相應(yīng)的module使用時才進(jìn)行創(chuàng)建夹攒。
JavaScriptModule接口類蜘醋,用于java調(diào)用js的接口,在rn中沒有實現(xiàn)類咏尝,具體如何使用后面再介紹压语。
JavaScriptModuleRegistry:JavaScriptModule的注冊表。
NativeModuleRegistry:NativeModule的注冊表编检,用于管理NativeModule列表胎食。
NativeModule:java暴露給js調(diào)用的api接口,如果想創(chuàng)建自己的module允懂,需要繼承這個接口厕怜。
ReactPackage:組件配置接口類,通過createNativeModules、createJSModules和createViewManagers等API去創(chuàng)建本地模塊粥航,JS模塊及視圖組件等琅捏。ReactPackage分為rn核心的CoreModulesPackage和業(yè)務(wù)方可選的基礎(chǔ)MainReactPackage類,其中CoreModulesPackage封裝了大部分通信功能递雀。
createReactContext函數(shù):
private ReactApplicationContext createReactContext(
JavaScriptExecutor jsExecutor,
JSBundleLoader jsBundleLoader) {
...
//初始化ReactApplicationContext實例
final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
try {
CoreModulesPackage coreModulesPackage =
new CoreModulesPackage(
this,
mBackBtnHandler,
mUIImplementationProvider,
mLazyViewManagersEnabled);
//將ReactPackage配置的modeles添加到nativeRegistryBuilder和jsModulesBuilder中
processPackage(
coreModulesPackage,
reactContext,
moduleSpecs,
reactModuleInfoMap,
jsModulesBuilder);
} finally {
...
}
...
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
.setJSExecutor(jsExecutor)
.setRegistry(nativeModuleRegistry)
.setJSModuleRegistry(jsModulesBuilder.build())
.setJSBundleLoader(jsBundleLoader)
.setNativeModuleCallExceptionHandler(exceptionHandler);
final CatalystInstance catalystInstance;
try {
catalystInstance = catalystInstanceBuilder.build();
} finally {
...
}
reactContext.initializeWithInstance(catalystInstance);
catalystInstance.runJSBundle();
return reactContext;
}
這個函數(shù)主要有三個功能:
1柄延、根據(jù)ReactPackage生成js module和native module的注冊表
2、創(chuàng)建CatalystInstance實例
3映之、加載bundle文件
生成注冊表代碼比較簡單拦焚,這里就不做介紹了,下面我們看CatalystInstanceImpl的構(gòu)造函數(shù):
private CatalystInstanceImpl(
final ReactQueueConfigurationSpec ReactQueueConfigurationSpec,
final JavaScriptExecutor jsExecutor,
final NativeModuleRegistry registry,
final JavaScriptModuleRegistry jsModuleRegistry,
final JSBundleLoader jsBundleLoader,
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
...
//native函數(shù)杠输,主要是為了將創(chuàng)建C++實例并將指針地址保存到j(luò)ava中
mHybridData = initHybrid();
mReactQueueConfiguration = ReactQueueConfigurationImpl.create(
ReactQueueConfigurationSpec,
new NativeExceptionHandler());
mBridgeIdleListeners = new CopyOnWriteArrayList<>();
mJavaRegistry = registry;
mJSModuleRegistry = jsModuleRegistry;
mJSBundleLoader = jsBundleLoader;
mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
mTraceListener = new JSProfilerTraceListener(this);
initializeBridge(
new BridgeCallback(this),
jsExecutor,
mReactQueueConfiguration.getJSQueueThread(),
mReactQueueConfiguration.getNativeModulesQueueThread(),
mJavaRegistry.getModuleRegistryHolder(this));
mMainExecutorToken = getMainExecutorToken();
}
構(gòu)造函數(shù)有有兩個重要的參數(shù):
1赎败、ReactQueueConfigurationSpec:用于配置消息線程,在rn中有三個消息線程:UI線程蠢甲、JS線程僵刮、Native線程,其中native調(diào)用js的代碼會JS線程運行鹦牛,JS調(diào)用native的代碼會在Native線程中執(zhí)行搞糕。
2、ModuleRegistryHolder:Native Module的封裝類曼追,將java層Native Module信息傳遞給c++層窍仰。
public class ModuleRegistryHolder {
private final HybridData mHybridData;
private static native HybridData initHybrid(
CatalystInstanceImpl catalystInstanceImpl,
Collection<JavaModuleWrapper> javaModules,
Collection<CxxModuleWrapper> cxxModules);
public ModuleRegistryHolder(CatalystInstanceImpl catalystInstanceImpl,
Collection<JavaModuleWrapper> javaModules,
Collection<CxxModuleWrapper> cxxModules) {
mHybridData = initHybrid(catalystInstanceImpl, javaModules, cxxModules);
}
}
上面已經(jīng)說過initHybrid的工作,此處會在創(chuàng)建C++中ModuleRegistryHolder對象礼殊。接著調(diào)用initializeBridge函數(shù)來初始化驹吮,initializeBridge是native方法:
CatalystInstanceImpl.cpp
void CatalystInstanceImpl::initializeBridge(
jni::alias_ref<ReactCallback::javaobject> callback,
// This executor is actually a factory holder.
JavaScriptExecutorHolder* jseh,
jni::alias_ref<JavaMessageQueueThread::javaobject> jsQueue,
jni::alias_ref<JavaMessageQueueThread::javaobject> moduleQueue,
ModuleRegistryHolder* mrh) {
instance_->initializeBridge(folly::make_unique<JInstanceCallback>(callback),
jseh->getExecutorFactory(),
folly::make_unique<JMessageQueueThread>(jsQueue),
folly::make_unique<JMessageQueueThread>(moduleQueue),
mrh->getModuleRegistry());
}
JavaScriptExecutorHolder:對應(yīng)于java層中的JavaScriptExecutor的C++對象,getExecutorFactory()返回JSCExecutorFactory對象晶伦,用于生成js解析器碟狞。
ModuleRegistryHolder:java中native module的包裝類,其構(gòu)造函數(shù):
ModuleRegistryHolder.cpp
ModuleRegistryHolder::ModuleRegistryHolder(
CatalystInstanceImpl* catalystInstanceImpl,
jni::alias_ref<jni::JCollection<JavaModuleWrapper::javaobject>::javaobject> javaModules,
jni::alias_ref<jni::JCollection<CxxModuleWrapper::javaobject>::javaobject> cxxModules) {
std::vector<std::unique_ptr<NativeModule>> modules;
std::weak_ptr<Instance> winstance(catalystInstanceImpl->getInstance());
for (const auto& jm : *javaModules) {
modules.emplace_back(folly::make_unique<JavaNativeModule>(jm));
}
for (const auto& cm : *cxxModules) {
modules.emplace_back(
folly::make_unique<CxxNativeModule>(winstance, std::move(cthis(cm)->getModule())));
}
registry_ = std::make_shared<ModuleRegistry>(std::move(modules));
}
}
將java層的對象分裝成JavaNativeModule和CxxNativeModule對象婚陪,并將生成的對象注冊到ModuleRegistry對象中族沃,ModuleRegistry和上面提高的NativeModuleRegistry功能相似,是C++端module的注冊表泌参。繼續(xù)往下走脆淹,會調(diào)用Instance的initializeBridge函數(shù):
Instance.cpp
void Instance::initializeBridge(
std::unique_ptr<InstanceCallback> callback,
std::shared_ptr<JSExecutorFactory> jsef,
std::shared_ptr<MessageQueueThread> jsQueue,
std::unique_ptr<MessageQueueThread> nativeQueue,
std::shared_ptr<ModuleRegistry> moduleRegistry) {
callback_ = std::move(callback);
jsQueue->runOnQueueSync(
[this, &jsef, moduleRegistry, jsQueue,
nativeQueue=folly::makeMoveWrapper(std::move(nativeQueue))] () mutable {
nativeToJsBridge_ = folly::make_unique<NativeToJsBridge>(
jsef.get(), moduleRegistry, jsQueue, nativeQueue.move(), callback_);
std::lock_guard<std::mutex> lock(m_syncMutex);
m_syncReady = true;
m_syncCV.notify_all();
});
}
MessageQueueThread對應(yīng)于java層MessageQueueThread,進(jìn)行線程轉(zhuǎn)換變成js thread沽一,在這個函數(shù)會調(diào)用NativeToJsBridge的構(gòu)造函數(shù)盖溺,NativeToJsBridge:Native調(diào)用JS的橋梁。
NativeToJsBridge.cpp
NativeToJsBridge::NativeToJsBridge(
JSExecutorFactory* jsExecutorFactory,
std::shared_ptr<ModuleRegistry> registry,
std::shared_ptr<MessageQueueThread> jsQueue,
std::unique_ptr<MessageQueueThread> nativeQueue,
std::shared_ptr<InstanceCallback> callback)
: m_destroyed(std::make_shared<bool>(false))
, m_mainExecutorToken(callback->createExecutorToken())
, m_delegate(
std::make_shared<JsToNativeBridge>(
this, registry, std::move(nativeQueue), callback)) {
std::unique_ptr<JSExecutor>
mainExecutor =
jsExecutorFactory->createJSExecutor(m_delegate, jsQueue);
m_mainExecutor = mainExecutor.get();
registerExecutor(m_mainExecutorToken, std::move(mainExecutor), jsQueue);
}
m_delegate是JsToNativeBridge锯玛,用于JS調(diào)用Native函數(shù),和NativeToJsBridge一起作為連接java和js通信的橋梁。這個構(gòu)造函數(shù)的主要是創(chuàng)建js的執(zhí)行器攘残,這里的mainExecutor對象對應(yīng)于JSCExecutor對象拙友,JSCExecutor構(gòu)造函數(shù)中對js的執(zhí)行環(huán)境進(jìn)行初始化,并且向JavaScriptCore中注冊了幾個c++的方法供js端調(diào)用歼郭,這里就不做介紹了遗契,有興趣的可以自己閱讀代碼。到這里initializeBridge整個函數(shù)就全部介紹完畢了病曾。
下面回到ReactInstanceManager中的createReactContext函數(shù)中牍蜂,接著調(diào)用CatalystInstanceImpl的runJSBundle方法吏饿,接著看runJSBundle函數(shù)僧鲁,發(fā)現(xiàn)runJSBundle函數(shù)會調(diào)用JSBundleLoader的loadScript方法熄阻,前面我們介紹過JSBundleLoader有三種方式蹂风,我們假設(shè)使用的時文件加載的方式袱结,跟著函數(shù)一步步走下去深滚,最后會走到CatalystInstanceImpl的loadScriptFromFile函數(shù)访忿,這個又是一個native方法撵幽,查看CatalystInstanceImpl.cpp的代碼是牢,最后進(jìn)入NativeToJsBridge.cpp中l(wèi)oadApplication方法:
NativeToJsBridge.cpp
void NativeToJsBridge::loadApplication(
std::unique_ptr<JSModulesUnbundle> unbundle,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL) {
//進(jìn)行線程轉(zhuǎn)換僵井,把函數(shù)拋到j(luò)s thread的隊列中
runOnExecutorQueue(
m_mainExecutorToken,
[unbundleWrap=folly::makeMoveWrapper(std::move(unbundle)),
startupScript=folly::makeMoveWrapper(std::move(startupScript)),
startupScriptSourceURL=std::move(startupScriptSourceURL)]
(JSExecutor* executor) mutable {
auto unbundle = unbundleWrap.move();
if (unbundle) {
executor->setJSModulesUnbundle(std::move(unbundle));
}
executor->loadApplicationScript(std::move(*startupScript),
std::move(startupScriptSourceURL));
});
}
JSCExecutor.cpp中的loadApplicationScript函數(shù):
JSCExecutor.cpp
void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> script, std::string sourceURL) {
//JavaScriptCore函數(shù),執(zhí)行js代碼
evaluateScript(m_context, jsScript, jsSourceURL);
if (m_delegate) {
bindBridge();
flush();
}
}
m_delegate為上面介紹的JsToNativeBridge對象驳棱,這里不為null批什,后面就會執(zhí)行bindBridge函數(shù):
JSCExecutor.cpp
void JSCExecutor::bindBridge() throw(JSException) {
auto global = Object::getGlobalObject(m_context);
auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
if (batchedBridgeValue.isUndefined()) {
throwJSExecutionException("Could not get BatchedBridge, make sure your bundle is packaged correctly");
}
auto batchedBridge = batchedBridgeValue.asObject();
m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject();
m_invokeCallbackAndReturnFlushedQueueJS = batchedBridge.getProperty("invokeCallbackAndReturnFlushedQueue").asObject();
m_flushedQueueJS = batchedBridge.getProperty("flushedQueue").asObject();
m_callFunctionReturnResultAndFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnResultAndFlushedQueue").asObject();
}
這個函數(shù)主要實現(xiàn)下面幾個功能:
1、從js執(zhí)行環(huán)境中取出全局變量__fbBatchedBridge放到global變量中社搅。
2驻债、將global中某些特定的函數(shù)對象映射到C++對象中,這樣我們就可以通過C++對象調(diào)用js的代碼罚渐,假設(shè)我們想要調(diào)用js端__fbBatchedBridge的flushQueue方法却汉,在C++中就可以使用m_flushedQueueJS->callAsFunction()就可以實現(xiàn),那么__fbBatchedBridge在js端到底是個什么東西那荷并?
查看js代碼合砂,在BatchBridge.js中找到了__fbBatchedBridge的定義
const MessageQueue = require('MessageQueue');
const BatchedBridge = new MessageQueue();
...
Object.defineProperty(global, '__fbBatchedBridge', {
configurable: true,
value: BatchedBridge,
});
從上面可以看出__fbBatchedBridge就是MessageQueue對象,后面再介紹MessageQueue源织。
到這里整個rn的初始化和bundle加載就基本上介紹完畢了翩伪,下面介紹一下java和js之間如何通信的。
二谈息、java和js之間通信的過程
2.1 java調(diào)用js過程
舉一個列子:假設(shè)我們想要執(zhí)行js端AppRegistry.runApplication()函數(shù)缘屹,我們應(yīng)該怎么辦那?
首先要拿到ReactContext實例侠仇,然后調(diào)用getJSModule方法拿到AppRegistry實例轻姿,最后調(diào)用runApplication方法就可以了犁珠,很簡單吧,那么看代碼里面如何實現(xiàn)的互亮?
ReactContext.getJSModule函數(shù):
public <T extends JavaScriptModule> T getJSModule(Class<T> jsInterface) {
...
return mCatalystInstance.getJSModule(jsInterface);
}
ReactContext中的getJSModule很簡單犁享,直接調(diào)用CatalystInstanceImpl的JavaScriptModule方法:
@Override
public <T extends JavaScriptModule> T getJSModule(ExecutorToken executorToken, Class<T> jsInterface) {
return Assertions.assertNotNull(mJSModuleRegistry)
.getJavaScriptModule(this, executorToken, jsInterface);
}
調(diào)用JavaScriptModuleRegistry的getJavaScriptModule方法,
JavaScriptModuleRegistry前面提過豹休,是管理js module的注冊表炊昆,接著看一下內(nèi)部實現(xiàn):
JavaScriptModuleRegistry.java
public synchronized <T extends JavaScriptModule> T getJavaScriptModule(
CatalystInstance instance,
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, instance, registration));
instancesForContext.put(moduleInterface, interfaceProxy);
return (T) interfaceProxy;
}
這個函數(shù)的流程比較簡單:首先判斷js module是否已經(jīng)生成了,如果已經(jīng)生成了就直接返回內(nèi)存中的對象威根,假設(shè)我們是第一次調(diào)用凤巨,那么就會執(zhí)行后面的方法,使用java的動態(tài)代理來生成js module的實例洛搀,關(guān)于動態(tài)代理敢茁,我們知道實際的處理邏輯都在InvocationHandler中invoke方法中執(zhí)行,直接看InvocationHandler的實現(xiàn)類JavaScriptModuleInvocationHandler中的invoke函數(shù):
@Override
public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
ExecutorToken executorToken = mExecutorToken.get();
...
NativeArray jsArgs = args != null ? Arguments.fromJavaArgs(args) : new WritableNativeArray();
mCatalystInstance.callFunction(
executorToken,
mModuleRegistration.getName(),
method.getName(),
jsArgs
);
return null;
}
invoke方法比較簡單姥卢,直接調(diào)用mCatalystInstance的callFunction(...)方法卷要,看著這里就比較明白了,調(diào)用AppRegistery的runApplictaion(args)方法實際上是調(diào)用CatalystInstance的callFuction(,"AppRegistry","runApplictaion",args)方法独榴。其中NativeArray類僧叉,用于rn中java端和C++端進(jìn)行數(shù)據(jù)傳遞的數(shù)據(jù)結(jié)構(gòu),主要目的是節(jié)省內(nèi)存棺榔,便于管理瓶堕,有興趣的可以自行閱讀源碼。
這時大概java層流程就大概清楚了症歇,我們看C++層郎笆,一步步往下走,最后會到NativeToJsBridge中的callFunction函數(shù):
NativeToJsBridge.cpp
void NativeToJsBridge::callFunction(
ExecutorToken executorToken,
std::string&& module,
std::string&& method,
folly::dynamic&& arguments) {
runOnExecutorQueue(executorToken, [module = std::move(module), method = std::move(method), arguments = std::move(arguments), tracingName = std::move(tracingName), systraceCookie] (JSExecutor* executor) {
executor->callFunction(module, method, arguments);
});
}
runOnExecutorQueue的函數(shù)講過忘晤,不清楚請往前看宛蚓,后面會執(zhí)行JSExecutor的callFunction函數(shù),其中module是想要調(diào)用js端對象的名稱设塔,method是js對象中的方法名稱凄吏,arguments是方法參數(shù)。JSCExecutor::callFunction函數(shù):
JSCExecutor.cpp
void JSCExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) {
auto result = [&] {
try {
return m_callFunctionReturnFlushedQueueJS->callAsFunction({
Value(m_context, String::createExpectingAscii(m_context, moduleId)),
Value(m_context, String::createExpectingAscii(m_context, methodId)),
Value::fromDynamic(m_context, std::move(arguments))
});
} catch (...) {
}
}();
callNativeModules(std::move(result));
}
m_callFunctionReturnFlushedQueueJS是js全局對象_fbBatchedBridge中的callFunctionReturnFlushedQueue對象闰蛔,對它調(diào)用callAsFunction方法來痕钢,相當(dāng)于執(zhí)行_fbBatchedBridge.callFunctionReturnFlushedQueue函數(shù)。callNativeModules函數(shù)主要的功能是將js的執(zhí)行結(jié)果返回給native函數(shù)序六,這里需要注意的是這個過程不是線性的任连,前面說過_fbBatchedBridge是MessageQueue對象,查看MessageQueue.js中的callFunctionReturnFlushedQueue函數(shù):
MessageQueue.js
MessageQueue
callFunctionReturnFlushedQueue(module: string, method: string, args: Array<any>) {
//es6的新特性箭頭函數(shù)
guard(() => {
this.__callFunction(module, method, args);
this.__callImmediates();
});
return this.flushedQueue();
}
MessageQueued的__callFunction函數(shù):
__callFunction(module: string, method: string, args: Array<any>) {
...
const moduleMethods = this._callableModules[module];
...
const result = moduleMethods[method].apply(moduleMethods, args);
...
return result;
}
MessageQueue內(nèi)部有一個js模塊的配置數(shù)組例诀,每個js模塊會在js加載時將自己注冊到MessageQueue配置表_callableModules中,它以moduleId為key随抠,module對象為value進(jìn)行存儲裁着,此時這個函數(shù)的功能就是調(diào)用js中module模塊中method方法。
到這里還有個問題:這個配置表是如何生成的拱她?這里還是以AppRegistry為例跨算,查看AppRegistry.js的代碼:
BatchedBridge.registerCallableModule(
'AppRegistry',
AppRegistry
);
在加載AppRegistry.js時會調(diào)用BatchedBridge.registerCallableModule方法,前面我們講過BatchedBridge對象就是MessageQueue對象椭懊,會調(diào)用MessageQueue.registerCallableModule函數(shù):
registerCallableModule(name: string, module: Object) {
this._callableModules[name] = module;
}
js端配置表如何生成到這里也就結(jié)束了,總結(jié)來說步势,就是js文件加載時每個模塊會將通過key和value的形式注冊到MessageQueue中氧猬,工java端調(diào)用。在java調(diào)用接口時需要注意的時模塊名稱必須和js注冊的名稱完全一樣坏瘩,否則就會找不到這個module盅抚,執(zhí)行不成功。到這里整個java調(diào)用js的過程就就結(jié)束了倔矾。接著介紹js到native的調(diào)用過程妄均。
2.2 js調(diào)用native過程
還是一個例子來介紹這一過程,在Android中有個常用的控件Toast哪自,那么js中如何調(diào)用native的Toast那丰包?在rn中對這個過程進(jìn)行了封裝,我們想顯示hello world壤巷,在js中只需要調(diào)用ToastAndroid.show('hello world', ToastAndroid.SHORT);就可以了邑彪,那么它是如何實現(xiàn)的那?查看ToastAndroid.js的代碼:
ar RCTToastAndroid = require('NativeModules').ToastAndroid;
var ToastAndroid = {
SHORT: RCTToastAndroid.SHORT,
LONG: RCTToastAndroid.LONG,
TOP: RCTToastAndroid.TOP,
BOTTOM: RCTToastAndroid.BOTTOM,
CENTER: RCTToastAndroid.CENTER,
show: function (
message: string,
duration: number
): void {
RCTToastAndroid.show(message, duration);
},
showWithGravity: function (
message: string,
duration: number,
gravity: number,
): void {
RCTToastAndroid.showWithGravity(message, duration, gravity);
},
};
module.exports = ToastAndroid;
調(diào)用ToastAndroid的show函數(shù)就會RCTToastAndroid中的show函數(shù)胧华,RCTToastAndroid是NativeModules.js中NativeModules對象寄症,查看NativeModules.js的代碼NativeModules是一個空的hashmap對象,key為moduleName的名稱矩动,value為module的對象有巧,下面看一下NativeModules是如何生成的:
NativeModules.js
if (global.nativeModuleProxy) {
NativeModules = global.nativeModuleProxy;
} else {
const bridgeConfig = global.__fbBatchedBridgeConfig;
(bridgeConfig.remoteModuleConfig || []).forEach((config: ModuleConfig, moduleID: number) => {
const info = genModule(config, moduleID);
if (!info) {
return;
}
if (info.module) {
NativeModules[info.name] = info.module;
} else {
defineLazyObjectProperty(NativeModules, info.name, {
get: () => loadModule(info.name, moduleID)
});
}
});
}
上面解析過BatchBridge.js,里面對全局的global對象進(jìn)行賦值為MessageQueue悲没,查看MessageQueue是否nativeModuleProxy對象篮迎,發(fā)現(xiàn)整個js中沒有找到nativeModuleProxy,但是有一點我們需要注意的是我們在JSCExecutor注冊了很多C++的函數(shù)供js端調(diào)用檀训,global中函數(shù)對象包含JSCExecutor注冊的C++中的函數(shù)柑潦,在JSCExecutor.cpp中nativeModuleProxy對應(yīng)這個C++端的getNativeModule函數(shù),查看getNativeModule函數(shù):
JSCExecutor.cpp
JSValueRef JSCExecutor::getNativeModule(JSObjectRef object, JSStringRef propertyName) {
...
return m_nativeModules.getModule(m_context, propertyName);
}
m_nativeModules是SCJNativeModules類對象峻凫,getModule函數(shù):
SCJNativeModules
JSValueRef JSCNativeModules::getModule(JSContextRef context, JSStringRef jsName) {
std::string moduleName = String::ref(context, jsName).str();
//緩存中是否存在渗鬼,如果存在直接返回緩存的配置表
const auto it = m_objects.find(moduleName);
if (it != m_objects.end()) {
return static_cast<JSObjectRef>(it->second);
}
//如果緩存中沒有則生成
auto module = createModule(moduleName, context);
if (!module.hasValue()) {
return Value::makeUndefined(context);
}
...
return static_cast<JSObjectRef>(result->second);
}
createModule函數(shù):
JSCNativeModules.cpp
folly::Optional<Object> JSCNativeModules::createModule(const std::string& name, JSContextRef context) {
//沒有產(chǎn)生過native配置表
if (!m_genNativeModuleJS) {
auto global = Object::getGlobalObject(context);
獲取js全局對象中__fbGenNativeModule屬性
m_genNativeModuleJS = global.getProperty("__fbGenNativeModule").asObject();
m_genNativeModuleJS->makeProtected();
//通過C++中NativeModule包裝類調(diào)用java端的代碼,生成native module實例荧琼,前面已經(jīng)說過譬胎,native module是采用懶惰加載的方式加載的差牛,在這塊可以體會到
m_moduleRegistry->moduleNames();
}
//生成配置表,這個配置表中有三類信息:常量堰乔,函數(shù)和函數(shù)類型
auto result = m_moduleRegistry->getConfig(name);
if (!result.hasValue()) {
return nullptr;
}
//調(diào)用js中的__fbGenNativeModule函數(shù)將生成的配置表中的函數(shù)信息傳遞到j(luò)s中
Value moduleInfo = m_genNativeModuleJS->callAsFunction({
Value::fromDynamic(context, result->config),
Value::makeNumber(context, result->index)
});
return moduleInfo.asObject().getProperty("module").asObject();
}
生成配置表的函數(shù)比較簡單偏化,這里不做分析,感興趣的同學(xué)可以自行閱讀代碼镐侯,生成配置結(jié)構(gòu)大致如下:
生成的配置表的格式大概是:
{ 'module名稱', ,{ '常量key':'常量value'} { 'show', 'showWithGravity' },
}
后面會回調(diào)給global對象的__fbGenNativeModule方法侦讨,生成NativeModules列表,這里就不做介紹苟翻,解感興趣的可以自行閱讀源碼韵卤,RCTToastAndroid.show(message, duration);最后實際上會變成MessageQueue.enqueueNativeCall(1,"show",{"hello world",0},null,null)函數(shù);
MessageQueue.js中的enqueueNativeCall函數(shù):
MessageQueue.js
enqueueNativeCall(moduleID: number, methodID: number, params: Array < any > , onFail: ? Function, onSucc : ? Function) {
...
//判斷是否可以調(diào)用native的方法,當(dāng)兩個相繼調(diào)用的函數(shù)超過MIN_TIME_BETWEEN_FLUSHES_MSs才會調(diào)用nativeFlushQueueImmediate方法
const now = new Date().getTime();
if (global.nativeFlushQueueImmediate &&
now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS) {
global.nativeFlushQueueImmediate(this._queue);
this._queue = [
[],
[],
[], this._callID
];
this._lastFlush = now;
}
...
}
nativeFlushQueueImmediate是C++注冊的本地函數(shù)崇猫,對應(yīng)于JSCExecutor::nativeFlushQueueImmediate函數(shù)沈条,一步步往下走下去最后會執(zhí)行java端JavaModuleWrapper類中的invoke方法:
JavaModuleWrapper.java
@DoNotStrip
public void invoke(ExecutorToken token, int methodId, ReadableNativeArray parameters) {
mMethods.get(methodId).invoke(mCatalystInstance, token, parameters);
}
mMethods:native module中包含的的函數(shù);
methodId:js端要執(zhí)行的函數(shù)名稱诅炉;
parameters:js端傳遞過來的參數(shù)蜡歹;
這個方法最后會調(diào)用ToastModule的show方法,整個js調(diào)用native流程到這里就介紹完畢了涕烧。