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

推薦する

Windows 10 + mysql 8.0.11 zipインストールチュートリアルの詳細

準備する: MySQL 8.0 Windows zip パッケージのダウンロード アドレス: htt...

Dockerコンテナとホストマシン間の8時間の差の問題を解決する

デプロイに docker-compose を使用すると、出力ログと関連イベントが検証されて出力される...

Linuxにおけるumaskコマンドの使用原理と計算方法の詳しい解説

目次umask umaskの使用法原理1. umask値2. ファイルディレクトリの最大権限3. 従...

vue3.0 で要素を使用するための完全な手順

序文: vue3.0の要素フレームワークを使用します。要素はvue2.0をサポートしており、vue3...

MySQLのネクストキーロックのロック範囲についての簡単な説明

序文ある日、突然 MySQL の次のキー ロックについて尋ねられ、私の即座の反応は次のようなものでし...

ハイパーリンクの幅と高さを直接設定できない問題の解決策

幅と高さを直接使用することはできません。 display:block; または display:in...

Nginx 経由で Tomcat9 クラスターを構築し、セッション共有を実現する

Nginx を使用して Tomcat9 クラスターを構築し、Redis を使用してセッション共有を実...

MySQL 5.7.21 履歴データディレクトリからデータを復元するチュートリアルの解凍バージョン

状況の説明: データベースが異常に起動およびシャットダウンしたため、サービスを再度起動したときに「起...

JavaScript関数の詳細な紹介

任意の数のステートメントを関数を通じてカプセル化することができ、いつでもどこでも呼び出して実行できま...

一般的なブラウザのユーザーエージェントの概要

1. 基礎知識: HTTP ヘッダー ユーザーエージェントユーザー エージェントは、ユーザー エージ...

MySQL で 1 つのテーブルのフィールドを使用して別のテーブルのフィールドを更新する方法

1. 1列を変更する 学生の更新、都市c s.city_name = c.name を設定します こ...

AIX マウント NFS の書き込み効率が低い場合の解決策

NFSが提供するサービスマウント: サーバー上で /usr/sbin/rpc.mountd サーボ ...

数百万のデータに対して MySQL クエリを最適化する 4 つの方法

目次1. 時間が経つにつれて限界が遅くなる理由2. 百万データシミュレーション1. 従業員テーブルと...

2列の水平タイムラインを実装するためのVueサンプルコード

目次1.コンポーネントtimelineH.vueを実装する2. コンポーネントの呼び出しこの記事では...

珍しいけれど役に立つJSテクニックをいくつか紹介します

序文プログラミング言語には通常、さまざまな隠されたトリックが含まれており、これらのトリックを上手に使...