この記事では、React Native の基本をすでに理解していることを前提とし、ネイティブと JavaScript が通信する際の内部動作に焦点を当てます。 メインスレッド始める前に、React Native には 3 つのメインスレッドがあることを知っておく必要があります。
さらに、一般的に、特に指定がない限り、各ネイティブモジュールには独自のGCDキューがあります(後述)。 *シャドウキューは実際にはスレッドというよりGCDキューに似ています ネイティブモジュールネイティブ モジュールの作成方法がわからない場合は、ドキュメントを読むことをお勧めします。 これは、JavaScript によって呼び出され、JavaScript を呼び出すこともできるネイティブ モジュール Person の例です。 @interface Person : NSObject <RCTBridgeModule> @終わり @implementation ロガー RCT_EXPORT_MODULE() RCT_EXPORT_METHOD(挨拶:(NSString *)名前) { NSLog(@"こんにちは、%@!", 名前); [_bridge.eventDispatcher sendAppEventWithName:@"greeted" 本文:@{ @"name": name }]; } @終わり ここでは、RCT_EXPORT_MODULE と RCT_EXPORT_METHOD という 2 つのマクロに焦点を当て、それらの展開内容、役割、動作について説明します。 RCT_EXPORT_MODULE([js_name])このメソッドの名前が示すように、モジュールをエクスポートしますが、この特定のコンテキストでのエクスポートとはどういう意味でしょうか? これは、ブリッジがモジュールを認識していることを意味します。 その定義は実は非常に単純です。 #RCT_EXPORT_MODULE(js_name) を定義します\ RCT_EXTERN void RCTRegisterModule(クラス); \ + (NSString \*)moduleName { return @#js_name; } \ + (void)ロード{RCTRegisterModule(self);} 次のことを行います:
RCT_EXPORT_METHOD(メソッド)このマクロはさらに興味深いもので、メソッドに何も追加せず、指定されたメソッドを宣言するだけでなく、新しいメソッドも作成します。新しい方法は次のようになります。 + (NSArray *)__rct_export__120 { @[ @"", @"log:(NSString *)message" ] を返します。 } これは、プレフィックス (__rct_export__) とオプションの js_name (この場合は空) を宣言の行番号と __COUNTER__ マクロと組み合わせて構築されます。 このメソッドの目的は、オプションの js_name とメソッド シグネチャを含む配列を返すことです。この js_name の役割は、メソッド名の競合を回避することです。 ランタイムこのセットアップ全体は、ブリッジに情報を提供して、エクスポートされたすべてのモジュールとメソッドを見つけられるようにするためのものですが、これはすべてロード時に行われます。次に、実行時にどのように使用されるかを見てみましょう。 ブリッジが初期化されたときの依存関係グラフは次のとおりです。 初期化モジュールRCTRegisterModule が行うことは、新しいブリッジをインスタンス化するときにクラスが見つかるように、クラスを配列にプッシュすることだけです。ブリッジは、配列内のすべてのモジュールを反復処理し、各モジュールのインスタンスを作成し、ブリッジ側のインスタンスへの参照を格納し、モジュール インスタンスにブリッジへの参照を与え (両側で相互に呼び出すことができるように)、モジュール インスタンスに実行するように指定されたキューがあるかどうかを確認し、ない場合は他のモジュールとは別の新しいキューを与えます。 NSMutableDictionary *modulesByName; // = ... (RCTGetModuleClasses() 内のクラス moduleClass) { // ... モジュール = [モジュールクラス new]; if ([モジュールがSelectorに応答します:@selector(setBridge:)]) { モジュール.bridge = 自己; modulesByName[モジュール名] = モジュール; // ... } 構成モジュールこれらのモジュールを取得したら、バックグラウンド スレッドで各モジュールのすべてのメソッドをリストし、__rct__export__ で始まるメソッドを呼び出して、メソッド シグネチャの文字列を取得します。これは、パラメータの実際の型がわかるので重要です。実行時には、パラメータの1つがIDであることしかわかりませんでしたが、この方法では、IDが実際にはNSStringであることがわかります。 符号なし整数メソッドカウント; メソッド *methods = class_copyMethodList(moduleClass, &methodCount); (符号なし整数 i = 0; i < methodCount; i++) { メソッド method = methods[i]; SELセレクター = method_getName(メソッド); if ([NSStringFromSelector(セレクタ) hasPrefix:@"__rct_export__"]) { IMP imp = method_getImplementation(メソッド); NSArray *エントリ = ((NSArray *(*)(id, SEL))imp)(_moduleClass, セレクタ); //... [moduleMethods addObject:/* メソッドを表すオブジェクト */]; } } JavaScriptエグゼキュータの設定JS エグゼキュータには、バックグラウンド スレッドで JS コードを初期化するなど、より複雑な作業を実行できる -setUp メソッドがあります。これにより、すべてのエグゼキュータではなく、アクティブなエグゼキュータのみが setUp メソッド呼び出しを受け取るため、作業もいくらか節約されます。 JSGlobalContextRef ctx = JSGlobalContextCreate(NULL); _context = [[RCTJavaScriptContext alloc] initWithJSContext:ctx]; JSON設定の挿入JSON 構成には、次の例のように、モジュールのみが含まれます。 この構成情報はJavaScript仮想マシンのグローバル変数として保存されるため、JSサイドブリッジが初期化されるときにこの情報を使用してモジュールを作成できます。 JavaScript コードの読み込みこれは非常に簡単で、指定したプロバイダーからソース コードをロードするだけです。通常、開発中はパッケージャーから、運用中はディスクからロードします。 JavaScriptコードの実行すべての準備が整ったら、アプリケーションのソース コードを JS 仮想マシンに読み込み、コードをコピーして解析し、実行できます。すべての CommonJS モジュールは最初の実行時に登録する必要があり、エントリ ファイルが必要です。 JSValueRef jsError = NULL; JSStringRef execJSString = JSStringCreateWithCFString((__bridge CFStringRef) スクリプト); JSStringRef jsURL = JSStringCreateWithCFString((__bridge CFStringRef)sourceURL.absoluteString); JSValueRef 結果 = JSEvaluateScript(strongSelf->_context.ctx, execJSString, NULL, jsURL, 0, &jsError); JSStringRelease(jsURL); JSStringRelease(execJSString); JavaScript のモジュールJS 側では、 react-native の NativeModules を通じて、以前の JSON 構成情報で構成されるモジュールを取得できるようになりました。 動作の仕組みとしては、メソッドを呼び出すと、モジュール名、メソッド名、すべてのパラメータを含むキューに入れられ、JavsScript 実行の最後にこのキューがネイティブ モジュールに渡されて実行されます。 通話サイクル上記のコードを使用してモジュールを呼び出すと、次のようになります。 呼び出しはネイティブから開始する必要があり、ネイティブが JS を呼び出します (この図は、JS が実行中の特定の瞬間のみをキャプチャしています)。実行プロセス中に、JS が NativeModules のメソッドを呼び出すため、この呼び出しはネイティブ側で実行する必要があるため、この呼び出しをキューに入れます。 JS の実行が完了すると、ネイティブ モジュールはキューに入れられたすべての呼び出しを反復処理し、それらの実行が完了すると、ブリッジを介してコールバックし (ネイティブ モジュールは _bridge インスタンスを介して enqueueJSCall:args: を呼び出すことができます)、再度 JS にコールバックします。 (プロジェクトをフォローしている方ならご存知でしょうが、以前はネイティブ -> JS からの呼び出しキューも存在し、vSYNC ごとにディスパッチされていましたが、起動時間を短縮するために削除されました) パラメータタイプネイティブから JS への呼び出しは簡単で、パラメータは NSArray として渡され、JSON データとしてエンコードされますが、JS からネイティブへの呼び出しではネイティブ型が必要であり、そのためには基本型 (int、float、chars など) をチェックしますが、前述のように、どのオブジェクト (構造体) でも、実行時に NSMthodSignature から十分な情報を取得できないため、型を文字列として保存します。 正規表現を使用してメソッド シグネチャから型を抽出し、RCTConvert クラスを使用して実際にオブジェクトを変換します。デフォルトでは、各型のメソッドが提供され、JSON 入力を必要な型に変換しようとします。 構造体でない限り、objc_msgSend を使用してメソッドを動的に呼び出します。arm64 には objc_msgSend_stret のバージョンがないため、NSInvocation を使用します。 すべてのパラメータを変換した後、別の NSInvocation を使用してターゲット モジュールとメソッドを呼び出します。 例: // 特定のモジュールに次のメソッドがある場合、例: `MyModule` RCT_EXPORT_METHOD(methodWithArray:(NSArray *) size:(CGRect)size) {} // JS から次のように呼び出します: 'NativeModules' が必要です。MyModule.method(['a', 1], { x: 0, y: 0, 幅: 200, 高さ: 100 }); // ネイティブに送信される JS キューは次のようになります。 // ** これは呼び出しのキューなので、すべてのフィールドは配列であることに注意してください ** @[ @[ @0 ], // モジュールID @[ @1 ], // メソッドID @[ // 引数 @[ @[@"a", @1], @{ @"x": @0、@"y": @0、@"幅": @200、@"高さ": @100 } ] ] ]; // これは次の呼び出しに変換されます(疑似コード) NSInvocation呼び出し call[args][0] = GetModuleForId(@0) 呼び出し[args][1] = GetMethodForId(@1) call[args][2] = obj_msgSend(RCTConvert, NSArray, @[@"a", @1]) call[args][3] = NSInvocation(RCTConvert, CGRect, @{ @"x": @0, ... }) 電話() スレッド前述のように、-methodQueue メソッドを実装するか、methodQueue プロパティを有効なキューとマージすることによって実行するキューを指定しない限り、各モジュールにはデフォルトで 1 つの GCD キューがあります。例外は ViewManagers* (RCTViewManager を拡張) で、これはデフォルトで Shadow Queue を使用します。また、特別なターゲット RCTJSThread はスレッドでありキューではないため、単なるプレースホルダーです。 (実際、ビュー マネージャーは例外ではありません。基本クラスがシャドウ キューをターゲット キューとして明示的に指定しているためです) 現在のスレッドルールは次のとおりです。
JS 呼び出しのバッチが受信されると、これらの呼び出しはターゲット キューごとにグループ化され、並列に呼び出されます。 // `buckets` 内の `queue` ごとに `calls` をグループ化します for (バケット内のIDキュー) { ディスパッチブロック_tブロック = ^{ NSOrderedSet *calls = [buckets objectForKey:queue]; for (NSNumber *indexObj 呼び出し内) { // 実際に呼び出す } }; if (キュー == RCTJSThread) { [_javaScriptExecutor は JavaScriptQueue 上でブロックを実行します]; } そうでない場合 (キュー) { キュー、ブロックをdispatch_asyncします。 } } 結論React Native ブリッジングの仕組みについての詳細な概要は以上です。これが、より複雑なモジュールを構築したい人や、コア フレームワークに貢献したい人の役に立つことを願っています。 React Native のコア原則 (React Native Bridge) の詳細な理解に関するこの記事はこれで終わりです。React Native の原則に関するその他のコンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。 以下もご興味があるかもしれません:
|
<<: Centos 6.9 に MySQL をインストールするための詳細なチュートリアル
>>: Linux における「!」の知られざる使用法のまとめ
1. まず、移行サーバー上のデータ ファイルを見つけます。MySQL 5.7 とデフォルトのインスト...
参考: Docker 公式 Redis ドキュメント1. 特別なバージョン要件がある場合は、redi...
このセクションでは、HTML のリスト要素について学習します。リストは、Web サイトのデザインにお...
主な機能は次のとおりです。製品情報を追加する製品情報を変更する単一の製品を削除する複数の製品を削除す...
HTMLタグの説明1. HTMLタグタグ: !DOCTYPE説明: HTML ドキュメントが準拠する...
目次実際のプロジェクトで遭遇する要件実装が間違っているところもある私は個人的に、実装するより良い方法...
序文:前回の記事では、注意深い学生であれば発見できたかもしれない DDL ステートメントの使用法を中...
序文最近、面接中に、MySQL の InnoDB エンジンがどのようにトランザクションを実装している...
サーバーの LNPM 環境をインストールして構成する場合、複数のバージョンの PHP の共存を考慮す...
(I) 方法 1: 事前にスクリプト タグ内に直接定義します。この HTML ファイルにのみ適用され...
ユニークな「About」ページ自分を他の人たちと差別化する素晴らしい方法は、本当にユニークな自己紹介...
問題を見つける最近、仕事で問題が発生しました。InnoDB タイプの SQL ファイルを実行すると、...
この記事では、接続エラー ECONNREFUSED を例に、Node.js がエラーを処理するプロセ...
目次概要ファイル記述子同期、非同期、Promise同期書き込み非同期書き込み(推奨)約束の書き方...
CSS の一部のプロパティの前には「*」または「_」が付きます。さまざまなブラウザを識別する例えば...