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データベースの基礎知識のまとめ

推薦する

CSS で水平方向と垂直方向に中央揃えする 10 の方法を教えます (要約)

面接には必需品、仕事でも必ず使います。うーん、誰でも分かるでしょう。これ以上何も言わずに、要約とレン...

React イベントバインディングの詳細

目次クラスコンポーネントイベントバインディング関数コンポーネントイベントバインディング要約するRea...

12個のJavascriptテーブルコントロール(DataGrid)が整理されています

DataGrid コントロールの DataSource プロパティがデザイン時に設定されている場合、...

Nginx を使用してクロスドメイン Vue 開発環境を処理する方法

1. 需要正しい Cookie 配信と SSO テストを確実に実行できるように、ローカル テスト ド...

MySQLでホワイトリストアクセスを設定する方法

MySQLでホワイトリストアクセスを設定する手順1. ログイン mysql -uroot -pmys...

Ubuntu 18.04 で MySQL のインストール時にパスワードが要求されない場合の解決方法

Ubuntu 1804 への MySQL 5.7 のインストールについて詳しく紹介します。 MySQ...

CSS 疑似クラス: 空っぽだと光る (サンプルコード)

最近私の記事を読んだ人なら誰でも、私が現在WeChatミニプログラムプロジェクトを担当しており、その...

MySql エラー 1698 (28000) の解決策

1. 問題の説明: MysqlERROR1698 (28000) の解決方法、新しくインストールされ...

Webデザインの経験:ナビゲーションシステムをシンプルにする

<br />友人と話し合っていたとき、フレームワークのレイヤー設計の中で最も核となるのは...

Nginx で Basic Auth ログイン認証を設定する方法

nginx でファイルサーバーを構築することもありますが、これは一般に公開されていますが、サーバーが...

MySQLでANDとORを組み合わせる問題を解決する

以下のように表示されます。 SELECT prod_name,prod_price FROM pro...

MySQLインデックスの詳細

1. インデックスの原則インデックスは、列内の特定の値を持つ行をすばやく見つけるために使用されます。...

Linuxでawkを使用する方法の詳細な説明

awk を学ぶ前に、sed、grep、tr、cut などのコマンドを学んでおく必要があります。これら...

Linux コマンドラインでメールを送信する 5 つの方法 (推奨)

シェル スクリプトで電子メールを作成する必要がある場合は、コマンド ラインから電子メールを送信する知...

Ubuntu 16.04 で FTP サーバーを構築するチュートリアル

Ubuntu 16.04 FTP サーバーをビルドするftpをインストールするftp をインストール...