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

推薦する

Ubuntu 18.04 で apt ソースを Alibaba Cloud ソースに変更する詳細なプロセス

目次序文: Ubuntu 18.04 は apt ソースを Alibaba Cloud ソースに変更...

MySQLデータベースに画像を保存するいくつかの方法

通常、ユーザーがアップロードした写真はデータベースに保存する必要があります。一般的に、解決策は 2 ...

Linux ドライバ開発でよく使われる関数 copy_from_user open read write の詳細な説明

目次Linux ドライバーの共通機能 (copy_from_user open read write...

...

CentOS7 ファイアウォールとオープンポートの簡単な使い方の簡単な紹介

概要(公式にはより詳しい説明があります) Firewalld は、ネットワーク接続またはインターフェ...

jQuery で従業員管理登録ページを実装する

この記事では、従業員管理登録ページを実装するためのjQueryの具体的なコードを例として紹介します。...

MySQL テーブルタイプ ストレージエンジンの選択

目次1. 現在のデータベース支出のストレージエンジンを表示する方法1:方法2: 2. ENGINE=...

MySQL、Oracle、SQL Server のページングクエリ例の分析

最近、Oracle、MySQL、SQL Server 2005 のデータ ページング クエリについて...

JavaScript のクロージャの問題の詳細な説明

クロージャは、純粋関数型プログラミング言語の伝統的な機能の 1 つです。クロージャをコア言語構造の不...

React forwardRefの使い方と注意点

これまで react.forwardRef は react の高階コンポーネントには適用できませんで...

docker compose helloworld を使い始めるための詳細なプロセス

前提条件Compose は、Docker コンテナをオーケストレーションするためのツールです。Doc...

HTML テーブルのオーバーフローの解決方法

テーブルが広い場合は、あふれてしまう可能性があります。たとえば、左と右の 2 つの div がありま...

uniappの無痛トークンリフレッシュ方法の詳細な説明

フロントエンドがインターフェースを要求すると、バックエンドでインターフェースが定義されます。ステータ...

JavaScript Canvas は動的なワイヤーフレーム効果を描画します

この記事では、JavaScript Canvasの動的なワイヤーフレーム効果を描画する具体的なコード...

シンプルな加算計算機の JavaScript 実装

この記事では、参考までに、加算計算機を実装するためのJavaScriptの具体的なコードを紹介します...