ReactNative Android源碼分析

這篇文章針對于對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流程到這里就介紹完畢了涕烧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末月而,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子议纯,更是在濱河造成了極大的恐慌景鼠,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件痹扇,死亡現(xiàn)場離奇詭異铛漓,居然都是意外死亡,警方通過查閱死者的電腦和手機鲫构,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進(jìn)店門浓恶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人结笨,你說我怎么就攤上這事包晰。” “怎么了炕吸?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵伐憾,是天一觀的道長。 經(jīng)常有香客問我赫模,道長树肃,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任瀑罗,我火速辦了婚禮胸嘴,結(jié)果婚禮上雏掠,老公的妹妹穿的比我還像新娘。我一直安慰自己劣像,他們只是感情好乡话,可當(dāng)我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著耳奕,像睡著了一般绑青。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上屋群,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天时迫,我揣著相機與錄音,去河邊找鬼谓晌。 笑死,一個胖子當(dāng)著我的面吹牛癞揉,可吹牛的內(nèi)容都是我干的纸肉。 我是一名探鬼主播,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼喊熟,長吁一口氣:“原來是場噩夢啊……” “哼柏肪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起芥牌,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤烦味,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后壁拉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谬俄,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年弃理,在試婚紗的時候發(fā)現(xiàn)自己被綠了溃论。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡痘昌,死狀恐怖钥勋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情辆苔,我是刑警寧澤算灸,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站驻啤,受9級特大地震影響菲驴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜骑冗,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一谢翎、第九天 我趴在偏房一處隱蔽的房頂上張望捍靠。 院中可真熱鬧,春花似錦森逮、人聲如沸榨婆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽良风。三九已至,卻和暖如春闷供,著一層夾襖步出監(jiān)牢的瞬間烟央,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工歪脏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留疑俭,地道東北人。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓婿失,卻偏偏與公主長得像钞艇,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子豪硅,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,851評論 2 361

推薦閱讀更多精彩內(nèi)容

  • 團隊的項目已經(jīng)使用RN哩照,有必要對React Native For Android有一個深入的了解,于是就寫了這篇文...
    boliangzhao閱讀 8,489評論 11 39
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,322評論 25 707
  • 我一直走在路上懒浮,等車來等風(fēng)來等你來飘弧,不忘記保持幸福的模樣,那是我最愛的砚著,相信也是你最愛的次伶。我最近被愛情友情背叛,深...
    那天那頁閱讀 160評論 0 0
  • 2017萬物伊始稽穆,又是即將春暖花開的季節(jié)学少,新一年的農(nóng)村淘寶即將再次啟程。 孔子曰:“溫故而知新秧骑,可以為師矣版确。”在這...
    moriarty77閱讀 378評論 0 1
  • 第139章回顧 “如果你到了伽羅大陸與魔族首領(lǐng)直接交手乎折,一定不要有任何猶豫绒疗,無論他如何迷惑你÷畛危” 師傅洪亮的聲音令...
    陳瀛Neptune閱讀 258評論 20 20