React NativeのstartReactApplicationメソッドの簡単な分析

React NativeのstartReactApplicationメソッドの簡単な分析

今回は、 RNの起動処理を整理しました。最後のstartReactApplication比較的複雑で、最終的にフロントエンドjsを実行する処理が含まれるため、別途抽出して独立した記事で分析しました。

まず、 startReactApplicationが呼び出される場所を見てみましょう。

mReactRootView.startReactApplication() を実行します。
    getReactNativeHost().getReactInstanceManager(), appKey, mLaunchOptions);

startReactApplicationrootViewで呼び出され、入力パラメータがinstanceManager、appKey、mLaunchOptionsであることがわかります。

startReactApplicationに従って、その呼び出しチェーンを調べます。

mReactInstanceManager.createReactContextInBackground() -> recreateReactContextInBackgroundInner() -> recreateReactContextInBackgroundFromBundleLoader() -> recreateReactContextInBackground() -> runCreateReactContextOnNewThread()

recreateReactContextInBackgroundは、 ReactInstanceManagerのメソッドであり、次の 2 つのことを行います。

1. 以下に示すように、 ReactContextInitParamsインスタンスinitParamsを作成します。その入力パラメータjsExecutorFactoryReactInstanceManagerの作成時に渡されます。

最終的なReactContextInitParams initParams =
    新しい ReactContextInitParams(jsExecutorFactory、jsBundleLoader);

2. runCreateReactContextOnNewThreadを呼び出す

runCreateReactContextOnNewThread ReactInstanceManagerメソッドであり、主に次の 2 つのことを行います。

  1. 新しいスレッドを作成し、新しいスレッドでcreateReactContextを通じてReactContextコンテキストを作成します。
  2. コンテキスト環境はsetupReactContextを通じて設定され、最後にAppRegistry.jsが呼び出されてアプリが起動します。

ReactContext を作成する

まず、それがどこで呼ばれているかを見てみましょう:

最終的なReactApplicationContext reactApplicationContext =
    作成Reactコンテキスト(
        initParams.getJsExecutorFactory().create()、
        initParams.getJsBundleLoader());

2 つの入力パラメータは、 JsExecutorFactoryによって作成されたJavaScriptExecutorインスタンスとJsBundleLoaderインスタンスです。

JavaScriptエグゼキュータ

startReactApplication最初の入力パラメータは、 ReactInstanceManagerインスタンスを取得するためのgetReactNativeHost().getReactInstanceManager()です。 RN アプリケーションには、 MainActivity作成時に作成されたReactInstanceManagerインスタンスが 1 つだけ存在します。

React Native の起動プロセスを振り返ると、作成プロセス中に実際に呼び出されるメソッドは次のとおりです。

ReactInstanceManager reactInstanceManager = builder.build()

builder ReactInstanceManagerBuilderです。このクラスのbuildメソッドに進むと、最終的にreturn new ReactInstanceManager(...)が実行されることがわかります。構築パラメータの 4 番目のパラメータはgetDefaultJSExecutorFactoryです。その定義に進みます。

 プライベート JavaScriptExecutorFactory getDefaultJSExecutorFactory(
      文字列 appName、文字列 deviceName、コンテキスト applicationContext) {
    試す {
      // JSCが含まれている場合は通常どおり使用します
      アプリケーションコンテキストが必要な場合は、SoLoader を初期化します。
      SoLoader.loadLibrary("jscexecutor");
      新しい JSCExecutorFactory(appName, deviceName) を返します。
    } キャッチ (UnsatisfiedLinkError jscE) { /* ... */ }
}

つまり、 ReactInstanceManagerBuilderを作成するときに、 JSCExecutorFactoryを作成し、そのcreateメソッドを呼び出してJSCExecutorを作成します。 JSCExecutorFactory JavaScriptExecutorFactoryインターフェイスを実装します。そのcreateメソッドは次のようになり、 JSCExecutorインスタンスを返します。

 @オーバーライド
  パブリックJavaScriptExecutor create()は例外をスローします{
    WritableNativeMap jscConfig = 新しい WritableNativeMap();
    jscConfig.putString("OwnerIdentity", "ReactNative");
    jscConfig.putString("AppIdentity", mAppName);
    jscConfig.putString("デバイスID", mDeviceName);
    新しい JSCExecutor(jscConfig) を返します。
  }

JSCExecutorの定義を見てみると、 JavaScriptExecutorクラスから継承していることがわかります。

ストリップしない
/* パッケージ */ クラス JSCExecutor は JavaScriptExecutor を拡張します {
  静的{
    SoLoader.loadLibrary("jscexecutor");
  }
  /* パッケージ */ JSCExecutor(ReadableNativeMap jscConfig) {
    スーパー(initHybrid(jscConfig));
  }
  @オーバーライド
  パブリック文字列getName() {
    「JSCExecutor」を返します。
  }
  プライベート静的ネイティブHybridData initHybrid(ReadableNativeMap jscConfig);
}

したがって、 createReactContextの最初のパラメーターはJSCExecutorインスタンスであり、これはSoLoaderによってロードされる C++ モジュールであることは明らかです。

Jsバンドルローダー

同様に、 return new ReactInstanceManager(...)構築パラメータの 5 番目のパラメータは、 JSBundleLoader.createAssetLoader(mApplication, mJSBundleAssetUrl, false)です。

定義を見ると、 JSBundleLoaderインスタンスを返し、 loadScriptメソッドをオーバーライドしていることがわかりました。

パブリック静的 JSBundleLoader アセットローダーを作成します (
    最終的なコンテキスト context、最終的な文字列 assetUrl、最終的なブール値 loadSynchronously) {
  新しい JSBundleLoader() を返す {
    @オーバーライド
    パブリック文字列loadScript(JSBundleLoaderDelegateデリゲート) {
      delegate.loadScriptFromAssets(context.getAssets(), assetUrl, loadSynchronously);
      assetUrl を返します。
    }
  };
}

JSCExecutorインスタンスとJSBundleLoaderインスタンスを作成したら、正式にcreateReactContextメソッドに入ります。

ReactContext を作成する

プライベートReactApplicationContext createReactContext(
  最終的な ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);

  CatalystInstanceImpl.Builder catalystInstanceBuilder = /* ... */

  試す {
    catalystInstance を catalystInstanceBuilder.build() でビルドします。
  } ついに { /* ... */ }

  reactContext.initializeWithInstance(catalystInstance);

  ターボモジュールマネージャー ターボモジュールマネージャー =
    新しい TurboModuleManager( /* ... */ )

  catalystInstance.setTurboModuleManager(turboModuleManager);

  mJSIModulePackage != null の場合 {
    catalystInstance.addJSIModules( /* ... */ );
  }

  catalystInstance.runJSBundle();
  reactContext を返します。

その中で、 reactContextが最初に作成され、 catalystInstanceBuilderを通じてcatalystInstanceが作成されます。次に、 reactContextcatalystInstance initializeWithInstanceメソッドを通じて関連付けられ、 catalystInstance初期化するための一連の作業が実行されます。最後に、メソッドcatalystInstance.runJSBundle()を入力します。

インスタンスを初期化する

getUIQueueThreadgetNativeModulesQueueThreadgetJSQueueThreadを呼び出すと、UI スレッド、NativeModules スレッド、JS スレッドの 3 つのスレッド キューが作成されます。

実行JSBundle

パブリック void runJSBundle() {
  mJSBundleLoader.loadScript(CatalystInstanceImpl.this);
  同期済み (mJSCallsPendingInitLock) {
    mAcceptCalls = true;
    (PendingJSCall 関数: mJSCallsPendingInit) {
      関数を呼び出します(これを);
    }
    mJSCallsPendingInit.clear();
    mJSBundleHasLoaded = true;
  }
  Systrace.registerListener(mTraceListener);
}

前に返されたmJSBundleLoaderを通じてloadScriptメソッドを実行します。

パブリック文字列loadScript(JSBundleLoaderDelegateデリゲート) {
  delegate.loadScriptFromAssets(context.getAssets(), assetUrl, loadSynchronously);
  assetUrl を返します。
}

loadScriptFromAssetsメソッドはCatalystInstanceImplにあります:

パブリック void loadScriptFromAssets(
    AssetManager assetManager、文字列 assetURL、ブール値 loadSynchronously) {
  mSourceURL = アセットURL;
  jniLoadScriptFromAssets(assetManager、assetURL、同期的にロード)。
}

ここでのassetURLは、 createAssetLoader mJSBundleLoaderを作成するときに渡され、その割り当て時間はreactInstanceManagerBuilderインスタンスで、 reactNativeHostインスタンスのcreateReactInstanceManagerメソッドによって行われます。開発者がMainApplication.javagetJSBundleFileメソッドをオーバーライドしてassetURLをカスタマイズしている場合は、その URL が使用されます。それ以外の場合は、 file://sdcard/myapp_cache/index.android.bundleなどのシステムのデフォルトが使用されます。

jniLoadScriptFromAssetsメソッドは C++ 側で定義され、js ファイルを読み取るために使用されます。なぜ C++ メソッドを Java コードから直接呼び出すことができるのでしょうか? この質問については、後で Java と C++ 間および Java と JS 間の通信を分析するときに説明します。

reactContext createReactContextを通じて作成され、 catalystInstanceインスタンスが作成され、その 2 つが関連付けられて、 js ファイルがcatalystInstanceを通じて読み取られます。次に、 setupReactContextステージに入ります。

セットアップReactコンテキスト

プライベートvoid setupReactContext(final ReactApplicationContext reactContext) {
    同期された(mAttachedReactRoots){
      触媒インスタンスを初期化します。
      (ReactRoot reactRoot : mAttachedReactRoots) の場合 {
        (reactRoot.getState().compareAndSet(ReactRoot.STATE_STOPPED、ReactRoot.STATE_STARTED)) の場合 {
          ルートビューをインスタンスにアタッチします(reactRoot);
        }
      }
    }
    UiThreadUtil.runOnUiThread() は、
      パブリックボイド実行() {
        リスナーを初期化します。
      }
    )
    reactContext.runOnJSQueueThread() は、
      パブリックボイド実行() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
      }
    )
    reactContext.runOnNativeModulesQueueThread() は、
      パブリックボイド実行() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
      }
    )
}

ここで何が起こっているか見てみましょう:

  • catalystInstance.initialize(): すべてのネイティブモジュールの初期化
  • attachmentRootViewToInstance(reactRoot): すべてのRootViewを描画し、対応するインスタンスに追加し、対応するリスニングイベントを設定します。
  • UIモジュール、JSモジュール、ネイティブモジュールのスレッドを作成し、JSモジュールとネイティブモジュールが配置されているスレッドの優先順位を設定します。

この記事の要約

createReactContext メソッドと setupReactContext メソッドのソース コードから始めて、RN startReactApplication メソッドの実行プロセスが分析されます。これには次の内容が含まれます。

createReactContext の主な機能は、 reactContextを作成し、 catalystInstanceインスタンスを作成し、その 2 つを関連付け、 catalystInstanceを通じて js ファイルを読み取ることです。

setupReactContext の主な機能は、すべてのネイティブ モジュールを初期化し、すべてのルートビューを描画し、UI モジュール、JS モジュール、ネイティブ モジュール スレッドを作成し、優先順位を設定することです。

React Native startReactApplication メソッドに関するこの記事はこれで終わりです。React Native startReactApplication に関するその他の関連コンテンツについては、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • React NativeのScrollViewプルダウンリフレッシュ効果
  • React Nativeの起動プロセスの詳細分析
  • React Nativeのカスタムルーティング管理に関する深い理解
  • React NativeプロジェクトでLottieアニメーションを使用する方法

<<:  Alibaba Cloud CentOS7 サーバーの nginx 構成と FAQ の分析

>>:  MySqlデータベースの基礎知識のまとめ

推薦する

Linux7で仮想ホストを実装する3つの方法

1. 同じIPアドレス、異なるポート番号仮想ホスト 1: ホスト IP アドレスは 172.16.3...

JavaScript の 7 つのデータ型の詳細な説明

目次序文:詳しい紹介:練習する:要約する序文: Python、Java、Cシリーズなど、すべての主要...

MYSQL 文字列強制変換メソッドの例

序文2 つのテーブル内の同じフィールドの型が異なっていたり、エンコード タイプが異なっていたりするた...

HTML ウェブページのブラウザタイトルバーに小さなアイコンを表示する方法

この効果と同様に、方法も非常に簡単です。ヘッダーに次のように記述します: <link rel=...

Vueでlessを使用する問題を解決する

1. less依存関係をインストールします: npm install less less-loade...

CSS で隠し要素を実現する 7 つの興味深い方法

序文非表示要素の 3 つの属性である表示、可視性、不透明度の類似点と相違点は、フロントエンドの就職面...

Vue が scss (mixin) をグローバルに導入

目次1. ミックスイン.scss 2. 単一ファイルの使用3. グローバルマウント3.1 依存関係の...

Docker ベースの Redis クラスターの構築方法

Redisイメージをダウンロードする docker pull yyyyttttwww/redis を...

XHTML チュートリアル: 初心者のための XHTML の基礎

<br />当サイトのオリジナルコンテンツですので、転載の際は出典を123WORDPRE...

CSS ロリポップを描くサンプルコード

背景: 毎日少しずつ進歩し、少しずつ積み重ねていけば、どんどん良くなっていきますコード: <!...

Nuxt.jsプロジェクトのDockerデプロイメントの実装

Docker 公式ドキュメント: https://docs.docker.com/ Docker は...

JavaScript 非同期プログラミングにおける Promise の初期の使用法の詳細な説明

1. 概要Promise オブジェクトは、ES6 で提案された非同期プログラミングの仕様です。非同期...

Linuxコマンドunzipの詳しい説明

目次1. 解凍コマンド1.1 構文1.2 オプション2. 例1. 解凍コマンドunzip コマンドは...

Mysql は、デッドロック問題を解決するために kill コマンドを使用します (実行中の特定の SQL ステートメントを強制終了します)。

MySQL を使用して特定のステートメントを実行すると、データ量が多いためにデッドロックが発生し、...

MySQL データベース インデックスの面接の質問 (基本的なプログラマー スキル)

目次導入インデックスの原則1. データページ2. ページディレクトリ3. インデックス原則分析要約す...