ReactNative Android源碼分析

團隊的項目已經使用RN,有必要對React Native For Android有一個深入的了解,于是就寫了這篇文章。注意本文分析的代碼版本為:

  • "react": "15.3.1",
  • "react-native": "0.32.0"

環境配置

首先按照React Native的官方文檔安裝必要的環境并下載Demo程序,請參考Getting Started. 為了更方便的修改RN的源碼,建議按照Building React Native from source配置一下。這篇文章第三步這是這樣

如果node_modules被刪除或重新npm install之前做的改動就沒有,建議自己下一份react-native的源碼,更換為本地絕對路徑。

從Demo入手

先把Demo跑起來看一下效果:


很簡單,就是幾個TextView. 我們來看一下JS是如何寫的,在index.android.js中

...

class AwesomeProject extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Text style={styles.instructions}>
          To get started, edit index.android.js
        </Text>
        <Text style={styles.instructions}>
          Double tap R on your keyboard to reload,{'\n'}
          Shake or press menu button for dev menu
        </Text>
      </View>
    );
  }
}

...

AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);

看著貌似很簡單,那他們是怎么生成Android的頁面呢。下面開始我們的分析之旅吧。

查看AwesomeProject下的android目錄,發現只有MainActivity和MainApplication兩個java文件。從MainActivity 入手

public class MainActivity extends ReactActivity {    
  /**     
   * Returns the name of the main component registered from JavaScript.     
   * This is used to schedule rendering of the component.    
   */
    @Override    
    protected String getMainComponentName() {        
        return "AwesomeProject";    
    }
}      

內容也很簡單,只有一個返回組件名字的函數。再關聯到上面的js文件,Component名字是一樣的。看一下ReactActvity的繼承它就是一個Activity的子類。頁面顯示什么內容完全是由它控制的。先看一下它的啟動時序圖:


ReactActivity啟動時序圖

接下來就到源碼里看看它的實現吧。

深入ReactAndroid

看了下ReactActivity的代碼,發現在它只是一個空殼子,方法都是交給ReactActivityDelegate去處理的。這個delegate在ReactActivity的構造函數里就創建了。看一下它的onCreate最主要的就是調用loadApp().

  protected void loadApp(String appKey) {
    if (mReactRootView != null) {
      throw new IllegalStateException("Cannot loadApp while app is already running.");
    }
    mReactRootView = createRootView();
    mReactRootView.startReactApplication(
      getReactNativeHost().getReactInstanceManager(),
      appKey,
      getLaunchOptions());
    getPlainActivity().setContentView(mReactRootView);
  }

最主要的是干了三件事:

  • 創建ReactRootView
  • 創建ReactInstanceManager
  • 啟動ReactApplication
    接下來逐一分析。
1. 創建ReactRootView
  protected ReactRootView createRootView() {
    return new ReactRootView(getContext());
  }

就是new了一個ReactRootView. 正如名字一樣,它就是一個View,繼承于FrameLayout。實現的功能包括,計算View的大小,監聽手勢、onTouch事件并交由JS處理,監聽View大小變化和鍵盤變化。

2. 創建ReactInstanceManager

ReactInstanceManager是用來創建及管理CatalyInstance的實例的上層接口、控制開發調試,生命周期與ReactRootView所在activity保持一致。使用ReactInstanceManager.Builder創建,準備的參數包括:

ReactInstanceManager.Builder builder = ReactInstanceManager.builder()
  .setApplication(mApplication)
  .setJSMainModuleName(getJSMainModuleName())
  .setUseDeveloperSupport(getUseDeveloperSupport())
  .setRedBoxHandler(getRedBoxHandler())
  .setUIImplementationProvider(getUIImplementationProvider())
  .setInitialLifecycleState(LifecycleState.BEFORE_CREATE);

for (ReactPackage reactPackage : getPackages()) {
  builder.addPackage(reactPackage);
}

String jsBundleFile = getJSBundleFile();
if (jsBundleFile != null) {
  builder.setJSBundleFile(jsBundleFile);
} else {
  builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
}
builder.build();
  • Application :Android的Context
  • Module Name : 對應index.android.js中的AppRegistry.registerComponent('Module Name'), 即是應用入口注冊的模塊名
  • DeveloperSupport和RedBox: 調試相關
  • UIImplementationProvider : 獲取一個UIImplementation,這個類用于接收JS命令,然后處理Android的View
  • ReactPackage: 包含一些Native和JS模塊,RN自己提供了一個MainPackage
  • BundleFile : JS代碼和資源,文件的位置如何確定稍后會介紹

build之后會生成XReactInstanceManagerImpl。

到此startReactApplication的所有參數都已經準備好了,來看一下它的實現

if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
  mReactInstanceManager.createReactContextInBackground();
}

主要實現就是判斷ReactContext是否已經創建,沒有就在后臺線程創建

3. 創建ReactContext
  /**
   * @return instance of {@link ReactContext} configured a {@link CatalystInstance} set
   */
  private ReactApplicationContext createReactContext(
      JavaScriptExecutor jsExecutor,
      JSBundleLoader jsBundleLoader) {

    mSourceUrl = jsBundleLoader.getSourceUrl();
    NativeModuleRegistry.Builder nativeRegistryBuilder = new NativeModuleRegistry.Builder();
    JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder();

    final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
    if (mUseDeveloperSupport) {
      reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);
    }

    try {
      CoreModulesPackage coreModulesPackage =
          new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);
      processPackage(coreModulesPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
    } finally {
    }

    for (ReactPackage reactPackage : mPackages) {
      try {
        processPackage(reactPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
      } finally {
      }
    }

    NativeModuleRegistry nativeModuleRegistry;
    try {
       nativeModuleRegistry = nativeRegistryBuilder.build();
    } finally {
    }

    NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null
        ? mNativeModuleCallExceptionHandler
        : mDevSupportManager;
    CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
        .setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
        .setJSExecutor(jsExecutor)
        .setRegistry(nativeModuleRegistry)
        .setJSModuleRegistry(jsModulesBuilder.build())
        .setJSBundleLoader(jsBundleLoader)
        .setNativeModuleCallExceptionHandler(exceptionHandler);

    ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_START);
    try {
      catalystInstance = catalystInstanceBuilder.build();
    } finally {
    }

    if (mBridgeIdleDebugListener != null) {
      catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
    }

    try {
      catalystInstance.getReactQueueConfiguration().getJSQueueThread().callOnQueue(
        new Callable<Void>() {
          @Override
          public Void call() throws Exception {
            reactContext.initializeWithInstance(catalystInstance);
            try {
              catalystInstance.runJSBundle();
            } finally {
            }
            return null;
          }
        }).get();
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    } catch (ExecutionException e) {
      if (e.getCause() instanceof RuntimeException) {
        throw (RuntimeException) e.getCause();
      } else {
        throw new RuntimeException(e);
      }
    }

    return reactContext;
  }

需要兩個參數:JavaScriptExecutor和JSBundleLoader,都是之前創建的,具體作用后續用到會解釋,先看一下繼承關系:

?ReactContext類圖

它就是一個普通的Android Context子類,使用的是Application Context.
這個方法的重點不是創建Context,而是創建CatalystInstance,用于鏈接Java與JS引擎。
這兩個方法調用了兩次processPackage。?

  • CoreModulesPackage

    • AndroidInfoModule : 獲取Android版本號和ServerHost
    • DeviceEventManagerModule:事件處理
    • ExceptionsManagerModule :異常處理
    • Timing :Timer,會監聽屏幕每幀的變化,做一些處理
    • SourceCodeModule: 可以獲取到Bundle的地址
    • UIManagerModule:UI管理器,允許JS創建和修改native的View
    • 一些debug相關的模塊
      還有一些JS Module接口,和JS模塊的名字一致,還沒有看JS代碼,具體功能就不描述了,看名字應該能猜出來
    • RCTDeviceEventEmitter
    • JSTimersExecution
    • RCTEventEmitter
    • RCTNativeAppEventEmitter
    • AppRegistry
    • Systrace
    • HMRClient
    • SamplingProfiler
    • RCTDebugComponentOwnership

  • MainPackage

    • AppStateModule :獲取App前后臺狀態
    • AsyncStorageModule:操作數據庫,保存的是catalyst的一些狀態
    • CameraRollManager :獲取設備相冊的照片
    • ClipboardModule : 允許JS操作剪貼板
    • DatePickerDialogModule :JS可以調起native的日期控件
    • DialogModule:JS調起native的對話框
    • FrescoModule:Fresco庫初始化和清理數據
    • I18nManagerModule :國際化相關
    • ImageEditingManager :圖片裁剪
    • ImageLoaderModule:圖片加載器
    • ImageStoreManager :圖片內存緩存
    • IntentModule : 打開其他的URL或Activity
    • LocationModule:提供JS獲取位置信息
    • NativeAnimatedModule :創建、管理Native的動畫
    • XMLHttpRequest :實現了JS的XMLHttpRequest接口
    • NetInfoModule :獲取網絡狀態信息
    • PermissionsModule : Android權限管理
    • ShareModule :Android自帶的分享
    • StatusBarModule :改變狀態欄樣式
    • TimePickerDialogModule : 調起Android選擇時間控件
    • ToastModule : 使用Android Toast
    • VibrationModule : 管理設備震動
    • WebSocketModule:基于OkHttp的ws實現
      該模塊中還一些ViewManager(創建管理Android的View),具體功能 先不介紹了,之后再專門寫一篇關于View的文章
    • ARTRenderableViewManager
    • ARTRenderableViewManager
    • ARTRenderableViewManager
    • ARTSurfaceViewManager
    • ReactDialogPickerManager
    • ReactDrawerLayoutManager
    • ReactDropdownPickerManager
    • ReactHorizontalScrollViewManager
    • ReactImageManager
    • ReactModalHostManager
    • ReactProgressBarViewManager
    • ReactRawTextManager
    • ReactScrollViewManager
    • ReactSliderManager
    • ReactSwitchManager
    • FrescoBasedReactTextInlineImageViewManager
    • ReactTextInputManager
    • ReactTextViewManager
    • ReactToolbarManager
    • ReactViewManager
    • ReactViewPagerManager
    • ReactVirtualTextViewManager
    • ReactWebViewManager
    • RecyclerViewBackedScrollViewManager
    • SwipeRefreshLayoutManager

列出上面這些是為了方便查看,以后開發時看能不能復用。回到先面說的processPackage,會把上面列出的module分別放到NativeModuleRegistry和JavaScriptModuleRegistry的map中。接下來就該創建CatalystInstance.來看一下它的構造函數:

private CatalystInstanceImpl(
    final ReactQueueConfigurationSpec ReactQueueConfigurationSpec,
    final JavaScriptExecutor jsExecutor,
    final NativeModuleRegistry registry,
    final JavaScriptModuleRegistry jsModuleRegistry,
    final JSBundleLoader jsBundleLoader,
    NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
  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();
}

終于看到一些核心點的東西了。先介看下第一個參數: ReactQueueConfigurationSpec,是用于配置執行消息的線程的,其它的參數上面都看到過。

ReactQueueConfigurationSpec

從這里猜測JS與Native之間交互,雙工通訊都有一個線程,每個線程有一個對列,特別像Android的Handler機制。看了一下ReactQueueConfigurationImpl.create的實現,確實是使用了Android的Handler機制,其準備好了三個線程:Android UI線程,JS 線程和NativeModulesQueue線程,后兩個后強線程并結合looper使用。第一線程很明顯是更新UI的,JS線程是JSCExecutor使用,native調用js會在這個線程執行,Native線程是執行JS調用native。
這里還有一個關鍵參數: ModuleRegistryHolder

mJavaRegistry.getModuleRegistryHolder(this)

這個要創建一個ModuleRegistryHolder對象,并把所有Native Module傳遞給c++層。

4. Module處理

執行到這里Native Module放在NativeModuleRegistry,JS Module放在JavaScriptModuleRegistry中,先來看一下它們相關的類圖

Native Module類圖

Native Module的信息在Java和C ++中都保存了一份。

?JS Module類圖

JS Module只在Java中保存。在JS代碼里應該會有Module的處理,這些Module真正的實現在JS中,Native調用只需要傳遞類名和方法名就可以。
JS Module在Native中只是一個接口,并未真正的實現

5. Native初始化

所有的參數準備好了之后,就需要初始化Bridge,這是JS與Native通訊的關鍵,是個native函數(注意這塊native的含意,對于RN來說,native是指Android或iOS,而在java或Android中Native就是指c/c++).
這里有一個細節,initializeBridge是一個jni方法,CatalystInstanceImpl.cpp只是一個空殼子,真的實現是在Instance.cpp中,應該是為了Android和iOS能復用。查看c++代碼,最終走到這里

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_);
    });

  CHECK(nativeToJsBridge_);
}

其實就是在上面創建的JS線程是創建一個c++層的NativeToJsBridge的對象,來看一下它的構造函數

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);
  // cached to avoid locked map lookup in the common case
  m_mainExecutor = mainExecutor.get();
  registerExecutor(m_mainExecutorToken, std::move(mainExecutor), jsQueue);
}

在對象初始化時還創建了一個JsToNativeBridge對象,通過代碼里的注釋我們可以知道:

  • JsToNativeBridge :管理JS調用Native
  • NativeToJsBridge : 管理Native調用JS,也管理相關的線程

先創建一個JSExecutor,來看一下實現

std::unique_ptr<JSExecutor> JSCExecutorFactory::createJSExecutor(
    std::shared_ptr<ExecutorDelegate> delegate, std::shared_ptr<MessageQueueThread> jsQueue) {
     return std::unique_ptr<JSExecutor>(
          new JSCExecutor(delegate, jsQueue, m_cacheDir, m_jscConfig));
}

創建了一個JSCExecutor對象

JSCExecutor::JSCExecutor(std::shared_ptr<ExecutorDelegate> delegate,
                         std::shared_ptr<MessageQueueThread> messageQueueThread,
                         const std::string& cacheDir,
                         const folly::dynamic& jscConfig) throw(JSException) :
    m_delegate(delegate),
    m_deviceCacheDir(cacheDir),
    m_messageQueueThread(messageQueueThread),
    m_jscConfig(jscConfig) {

  initOnJSVMThread();
  SystraceSection s("setBatchedBridgeConfig");
  folly::dynamic nativeModuleConfig = folly::dynamic::array();
  {
    SystraceSection s("collectNativeModuleNames");
    std::vector<std::string> names = delegate->moduleNames();
    for (auto& name : delegate->moduleNames()) {
      nativeModuleConfig.push_back(folly::dynamic::array(std::move(name)));
    }
  }

  folly::dynamic config =
    folly::dynamic::object
      ("remoteModuleConfig", std::move(nativeModuleConfig));

  SystraceSection t("setGlobalVariable");
  setGlobalVariable(
    "__fbBatchedBridgeConfig",
    folly::make_unique<JSBigStdString>(detail::toStdString(folly::toJson(config))));
  setGlobalVariable(
    "__fbBatchedBridgeSerializeNativeParams",
    folly::make_unique<JSBigStdString>(""));
}

這里把之前傳到c++的NativeModule列表遍歷一下,弄到一個數據里,然后生成一個JSON串。

void JSCExecutor::setGlobalVariable(std::string propName, std::unique_ptr<const JSBigString> jsonValue) {
  try {
    SystraceSection s("JSCExecutor.setGlobalVariable",
                      "propName", propName);

    auto globalObject = JSContextGetGlobalObject(m_context);
    String jsPropertyName(propName.c_str());

    String jsValueJSON = jsStringFromBigString(*jsonValue);
    auto valueToInject = JSValueMakeFromJSONString(m_context, jsValueJSON);
    
    JSObjectSetProperty(m_context, globalObject, jsPropertyName, valueToInject, 0, NULL);
    
  } catch (...) {
    std::throw_with_nested(std::runtime_error("Error setting global variable: " + propName));
  }
}

先來看一下__fbBatchedBridgeConfig的值是什么

{
  "remoteModuleConfig":[
      ["ImageStoreManager"],
      ["ImageEditingManager"],
      ["LocationObserver"],
      ["Networking"],
      ["SourceCode"],
      ["NetInfo"],
      ["ShareModule"],
      ["ImageLoader"],
      ["FrescoModule"],
      ["ToastAndroid"],
      ["Timing"],
      ["Vibration"],
      ["UIManager"],
      ["DatePickerAndroid"],
      ["AsyncSQLiteDBStorage"],
      ["I18nManager"],
      ["AppState"],
      ["NativeAnimatedModule"],
      ["CameraRollManager"],
      ["PermissionsAndroid"],
      ["TimePickerAndroid"],
      ["AndroidConstants"],
      ["StatusBarManager"],
      ["ExceptionsManager"],
      ["AnimationsDebugModule"],
      ["DialogManagerAndroid"],
      ["IntentAndroid"],
      ["WebSocketModule"],
      ["Clipboard"],
      ["DeviceEventManager"]
  ]
}

這些就是我們上面列出的Module名字.
setGlobalVariable像是扔到一個全局的配置中,至此initializeBridge分析完了。
回到createReactContext中,看一下剩下部分

    try {
      catalystInstance.getReactQueueConfiguration().getJSQueueThread().callOnQueue(
        new Callable<Void>() {
          @Override
          public Void call() throws Exception {
            reactContext.initializeWithInstance(catalystInstance);

            Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "runJSBundle");
            try {
              catalystInstance.runJSBundle();
            } finally {
              Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
              ReactMarker.logMarker(RUN_JS_BUNDLE_END);
            }
            return null;
          }
        }).get();
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    } catch (ExecutionException e) {
      if (e.getCause() instanceof RuntimeException) {
        throw (RuntimeException) e.getCause();
      } else {
        throw new RuntimeException(e);
      }
    }

這段代碼最主要就是在JS的線種隊列中添加一個Callable,然后執行,最終調用到CatalystInstanceImpl.loadScriptFromFile(這里有三種實現,FromFile, FromAssets, FromOptimizedBundle,本次分析就選擇從File中加載,其原理是一樣的),也就是要加載JS代碼了。這是一個native方法,經過幾次調用會走到下面的方法

void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> script, std::string sourceURL) throw(JSException) {
  ...
  String jsScript = jsStringFromBigString(*script);
  ...
  String jsSourceURL(sourceURL.c_str());
  evaluateScript(m_context, jsScript, jsSourceURL);

  bindBridge();

  flush();
  ReactMarker::logMarker("CREATE_REACT_CONTEXT_END");
}

evaluateScript應該是調用了JavaScriptCore中的方法,驗證JS文件是否有效u并解析執行,bindBridge從__fbBatchedBridge中取出幾個屬性:

  • callFunctionReturnFlushedQueue
  • invokeCallbackAndReturnFlushedQueue
  • flushedQueue
    這三個都是MessageQueue.js中的方法,把它們當用c++的對象,保存在JSCExecutor中,關于這三個方法是如何設置的請參考React Native通訊原理
void JSCExecutor::flush() {
  auto result = m_flushedQueueJS->callAsFunction({});
  try {
    auto calls = Value(m_context, result).toJSONString();
    m_delegate->callNativeModules(*this, std::move(calls), true);
  } catch (...) {
    std::string message = "Error in flush()";
    try {
      message += ":" + Value(m_context, result).toString().str();
    } catch (...) {
      // ignored
    }
    std::throw_with_nested(std::runtime_error(message));
  }
}

先調用JS的flushedQueue,返回一個JS的一個隊列(本次拿到的隊列為空),對于RN如何把JS對象轉換成Native的對象,之后再寫篇文章分析。
至此createReactContext就完成了。

6. setupReactContext

創建ReactApplicationContext是在AsyncTask的doInBackground中執行的,?而在onPostExecute中只調用了setupReactContext,看一下它的實現

  private void setupReactContext(ReactApplicationContext reactContext) {
    ...
    CatalystInstance catalystInstance =
        Assertions.assertNotNull(reactContext.getCatalystInstance());

    catalystInstance.initialize();
    mDevSupportManager.onNewReactContextCreated(reactContext);
    mMemoryPressureRouter.addMemoryPressureListener(catalystInstance);
    moveReactContextToCurrentLifecycleState();

    for (ReactRootView rootView : mAttachedRootViews) {
      attachMeasuredRootViewToInstance(rootView, catalystInstance);
    }

    ReactInstanceEventListener[] listeners =
      new ReactInstanceEventListener[mReactInstanceEventListeners.size()];
    listeners = mReactInstanceEventListeners.toArray(listeners);

    for (ReactInstanceEventListener listener : listeners) {
      listener.onReactContextInitialized(reactContext);
    }
    Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
  }
  • catalystInstance.initialize()會初始化所有Native Module
  • 添加內存警告的回調
  • 如果是Resumed狀態,回調listener
  • 給root view添加內容,重點看一下這個
 private void attachMeasuredRootViewToInstance(
     ReactRootView rootView,
     CatalystInstance catalystInstance) {
   Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "attachMeasuredRootViewToInstance");
   UiThreadUtil.assertOnUiThread();

   // Reset view content as it's going to be populated by the application content from JS
   rootView.removeAllViews();
   rootView.setId(View.NO_ID);

   UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class);
   int rootTag = uiManagerModule.addMeasuredRootView(rootView);
   rootView.setRootViewTag(rootTag);
   @Nullable Bundle launchOptions = rootView.getLaunchOptions();
   WritableMap initialProps = Arguments.makeNativeMap(launchOptions);
   String jsAppModuleName = rootView.getJSModuleName();

   WritableNativeMap appParams = new WritableNativeMap();
   appParams.putDouble("rootTag", rootTag);
   appParams.putMap("initialProps", initialProps);
   catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
   Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
 }

設置RootView,然后獲取AppRegistry的代理對象,因為所有的JSModule在Android中都是接口,無法直接調用,所以就使用了代理對象。這樣也有一個好處就是,所有Native調用JS都會收斂到JavaScriptModuleInvocationHandler中,有一個統一的入口

    public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
      ExecutorToken executorToken = mExecutorToken.get();
      if (executorToken == null) {
        FLog.w(ReactConstants.TAG, "Dropping JS call, ExecutorToken went away...");
        return null;
      }
      NativeArray jsArgs = args != null ? Arguments.fromJavaArgs(args) : new WritableNativeArray();
      mCatalystInstance.callFunction(
        executorToken,
        mModuleRegistration.getName(),
        method.getName(),
        jsArgs
      );
      return null;
    }

從而得知,所有native調用js都是通過CatalystInstance的。在callFunction中會做一些容錯處理,然后調用c++方法callJSFunction。

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) {
    
    ....

    // This is safe because we are running on the executor's thread: it won't
    // destruct until after it's been unregistered (which we check above) and
    // that will happen on this thread
    executor->callFunction(module, method, arguments);
  });
}
------------------------------------------------
void JSCExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) {

  try {
    auto result = m_callFunctionReturnFlushedQueueJS->callAsFunction({
      Value(m_context, String::createExpectingAscii(moduleId)),
      Value(m_context, String::createExpectingAscii(methodId)),
      Value::fromDynamic(m_context, std::move(arguments))
    });
    auto calls = Value(m_context, result).toJSONString();
     __android_log_print(ANDROID_LOG_DEBUG,"zbljni","JSCExecutor::callFunction moduleId = %s, methodId = %s, result = %s" , moduleId.c_str(), methodId.c_str(), calls.c_str());
    m_delegate->callNativeModules(*this, std::move(calls), true);
  } catch (...) {
    std::throw_with_nested(std::runtime_error("Error calling function: " + moduleId + ":" + methodId));
  }
}

m_callFunctionReturnFlushedQueueJS (這個方法的作用參考React Native通訊原理) 就是當作一個函數指針然后調用JS的代碼,也就是調用AppRegistry的runApplication,就是顯示

AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);

這里的Component。這樣JS代碼就能顯示到手機上了。UI相關的東西我們之后再單寫文章進行分析。
先看一下執行的結果

D/zbljni (11286): JSCExecutor::callFunction moduleId = AppRegistry, methodId = runApplication, result = [[4,4],[9,9],["[2,[3,15]]","[1,[2]]"],21]

一堆數字,這是什么鬼,看一下是怎么回調native的

void callNativeModules(
      JSExecutor& executor, std::string callJSON, bool isEndOfBatch) override {
    ExecutorToken token = m_nativeToJs->getTokenForExecutor(executor);
    m_nativeQueue->runOnQueue([this, token, callJSON=std::move(callJSON), isEndOfBatch] {
      // An exception anywhere in here stops processing of the batch.  This
      // was the behavior of the Android bridge, and since exception handling
      // terminates the whole bridge, there's not much point in continuing.
      for (auto& call : react::parseMethodCalls(callJSON)) {
        m_registry->callNativeMethod(
          token, call.moduleId, call.methodId, std::move(call.arguments), call.callId);
      }
      if (isEndOfBatch) {
        m_callback->onBatchComplete();
        m_callback->decrementPendingJSCalls();
      }
    });
  }

前面分析過有三個線程,這里就是在NativeModulesQueue線程中執行,里面有個parseMethodCalls,相信這是解析執行結果的,代碼在MethodCall.cpp中的parseMethodCalls就不粘代碼了, 先來解釋下返回結果的含意:

  • [4,4] : Module Id, 是c++層ModuleRegistry的modules_的索引
  • [9,9] : Method Id,是Java層JavaModuleWrapper的mMethods的索引
  • ["[2,[3,15]]","[1,[2]]"] : Request Params
  • 21 : Request Call Id

根據返回結果創建一個c++的 std::vector<MethodCall>,從上面的返回結果,應該是兩次請求,所有會有兩個MethodCall。

void ModuleRegistry::callNativeMethod(ExecutorToken token, unsigned int moduleId, unsigned int methodId,folly::dynamic&& params, int callId) {
  
  ...

  modules_[moduleId]->invoke(token, methodId, std::move(params));
}

這里modules_是所有Native Module的集合, 至于moduleId是否對應__fbBatchedBridgeConfig中的module還不確定,invoke方法在父類BaseJavaModule中就是執行module中的@ReactMethod的方法,如果有多個應該是由methodid決定的。

通過debug代碼,看到 4:對應的是UIManagerModule, 9 :對應的是setChildren,["[2,[3,15]]","[1,[2]]" :這些對應方法的參數

public void setChildren(  int viewTag,  ReadableArray childrenTags) {
  ...
}

還有?個疑問,在調用setChildren之前還調用了很多其他的方法,為什么runApplication的返回結果偏偏是它。由于對JS代碼不熟悉,所以猜測在調用runApplication之后,JS開始調用native創建UI,這些操作都是添加到JS的工作隊列中。由于時序問題,剛好runApplication執行完,隊列中需要執行setChildren,所以就返回回來了。如果有了解的朋友歡迎解惑。

其實,runApplication的調用就是Native與JS通訊的流程。

這篇文章只是從代碼的角度去分析,并未做相關的總結,之后會寫相關的文章去介紹。如果有感興趣的topic,歡迎留言。

已完成topic:

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,208評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,477評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,960評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,200評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,726評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,617評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,807評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,049評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,432評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,769評論 2 372

推薦閱讀更多精彩內容