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

推薦する

CentOS 6 は Docker を使用して Zookeeper 操作例を展開します

この記事では、Docker を使用して Centos6 に Zookeeper をデプロイする方法に...

JDBC 接続 (MySQL への接続) の 6 ステップのサンプル コード

JDBC の 6 つのステップ: 1. ドライバーを登録する2. データベース接続を取得する3. デ...

Windows 10 Home Edition に Docker をインストールする方法

最近、プロジェクトをアップグレードするために Docker を使用しました。これまで使用したことがな...

Docker のホスト間コンテナ通信オーバーレイ実装プロセスの詳細な説明

サーバーも 2 つあります。準備:コンテナのホスト名を設定する consul: kv タイプのストレ...

CSS における重要なカスケード概念の詳細な説明

最近、プロジェクトの過程で問題に遭遇しました。メニューバーを常に上部に表示し、後続の要素をその下に表...

ブラウザのURLの前に小さなアイコンを表示する方法

多くのウェブサイトを閲覧すると、ブラウザのアドレスバーの前に小さなアイコンがあり、ブラウザのタブの位...

MySQLデータベースのトランザクションとインデックスの詳細な説明

目次1. 事務:取引の 4 つの主な特徴:同時トランザクションはどのような問題を引き起こしますか? ...

MySql データベースにおける単一テーブル クエリと複数テーブル結合クエリの効率の比較

この間、プロジェクトに取り組んでいるときに、データ間の接続が非常に複雑なモジュールに遭遇しました。テ...

CSS 動的読み込みバー効果のサンプルコード

CSS変数の知識を使って、追加したコードとコメントを直接投稿します <!DOCTYPE htm...

JavaScript を使って簡単な計算機を書く

効果は以下のとおりです。参考プログラム: <!DOCTYPE html> <htm...

フォーム送信ページの更新がジャンプしない

1. ソースコードの設計コードをコピーコードは次のとおりです。 <!DOCTYPE html ...

CSS3はさまざまな境界効果を実現します

半透明の境界線結果: 実装コード: <div> 半透明の境界線が見えますか? </...

アバター変更機能を実装するJavaScript

この記事では、アバター変更機能を実装するためのJavaScriptの具体的なコードを参考までに共有し...

Raspberry PiにDockerをインストールする方法

Raspberry Pi は ARM アーキテクチャをベースとしているため、Docker のインスト...