In diesem Artikel gehen wir davon aus, dass Sie bereits mit den Grundlagen von React Native vertraut sind, und konzentrieren uns auf die Funktionsweise der Kommunikation zwischen nativer Version und JavaScript. HauptthreadBevor wir beginnen, müssen wir wissen, dass es in React Native drei Hauptthreads gibt:
Darüber hinaus verfügt jedes native Modul im Allgemeinen über eine eigene GCD-Warteschlange, sofern nicht anders angegeben (wird später erläutert). *Die Schattenwarteschlange ähnelt eigentlich eher einer GCD-Warteschlange als einem Thread Natives ModulWenn Sie nicht wissen, wie Sie ein natives Modul erstellen, empfehle ich Ihnen, die Dokumentation zu lesen. Dies ist ein Beispiel für ein natives Modul „Person“, das sowohl von JavaScript aufgerufen wird als auch JavaScript aufrufen kann. @interface Person : NSObject <RCTBridgeModul> @Ende @implementation Logger RCT_EXPORT_MODULE() RCT_EXPORT_METHOD(Begrüßung:(NSString *)Name) { NSLog(@"Hallo, %@!", Name); [_bridge.eventDispatcher sendAppEventWithName:@"begrüßt" body:@{ @"name": name }]; } @Ende Wir konzentrieren uns auf die beiden Makros RCT_EXPORT_MODULE und RCT_EXPORT_METHOD, worauf sie sich erweitern, welche Rollen sie spielen und wie sie funktionieren. RCT_EXPORT_MODULE([js_name])Wie der Name dieser Methode schon andeutet, exportiert sie Ihr Modul. Aber was bedeutet „Exportieren“ in diesem speziellen Kontext? Es bedeutet, dass die Bridge über Ihr Modul Bescheid weiß. Die Definition ist eigentlich ganz einfach: #define RCT_EXPORT_MODULE(js_name) \ RCT_EXTERN void RCTRegisterModule(Klasse); \ + (NSString \*)Modulname { return @#js_name; } \ + (void)load { RCTRegisterModule(self); } Es hat folgende Funktion:
RCT_EXPORT_METHOD(Methode)Dieses Makro ist interessanter, da es Ihrer Methode nichts hinzufügt. Es deklariert nicht nur die angegebene Methode, sondern erstellt auch eine neue Methode. Die neue Methode sieht folgendermaßen aus: + (NSArray *)__rct_export__120 { zurück @[ @"", @"log:(NSString *)message" ]; } Es wird durch die Kombination eines Präfixes (__rct_export__) und eines optionalen js_name (in diesem Fall leer) mit der Zeilennummer der Deklaration und dem Makro __COUNTER__ erstellt. Der Zweck dieser Methode besteht darin, ein Array zurückzugeben, das einen optionalen js_name und eine Methodensignatur enthält. Die Rolle dieses js_name besteht darin, Konflikte bei der Methodenbenennung zu vermeiden. LaufzeitDieses gesamte Setup dient lediglich dazu, Informationen für die Bridge bereitzustellen, damit sie alle exportierten Module und Methoden finden kann. Dies geschieht jedoch alles zur Ladezeit. Sehen wir uns nun an, wie es zur Laufzeit verwendet wird. Hier ist das Abhängigkeitsdiagramm, wenn die Brücke initialisiert wird: InitialisierungsmodulRCTRegisterModule schiebt die Klasse lediglich in das Array, sodass sie beim Instanziieren einer neuen Brücke gefunden werden kann. Die Brücke durchläuft alle Module im Array, erstellt für jedes Modul eine Instanz, speichert auf der Brückenseite eine Referenz auf die Instanz, weist der Modulinstanz eine Referenz auf die Brücke zu (damit wir uns auf beiden Seiten gegenseitig aufrufen können), prüft dann, ob für die Modulinstanz eine Warteschlange zur Ausführung angegeben ist, und weist ihr andernfalls eine neue, von den anderen Modulen getrennte Warteschlange zu: NSMutableDictionary *modulesByName; // = ... für (Klasse ModulKlasse in RCTGetModuleClasses()) { // ... Modul = [Modulklasse neu]; wenn ([Modul antwortet auf Selector:@selector(setBridge:)]) { modul.bridge = selbst; moduleByName[Modulname] = Modul; // ... } KonfigurationsmodulSobald wir diese Module haben, listen wir in einem Hintergrund-Thread alle Methoden jedes Moduls auf und rufen dann die Methode auf, die mit __rct__export__ beginnt. Wir erhalten eine Zeichenfolge mit der Methodensignatur. Dies ist wichtig, da wir nun den tatsächlichen Typ des Parameters kennen. Zur Laufzeit wussten wir nur, dass einer der Parameter eine ID war, aber auf diese Weise können wir wissen, dass die ID tatsächlich ein NSString ist * vorzeichenlose int Methodenanzahl; Methode *Methoden = class_copyMethodList(Modulklasse, &Methodenanzahl); für (unsigned int i = 0; i < methodCount; i++) { Methode Methode = Methoden[i]; SEL-Selektor = method_getName(Methode); wenn ([NSStringFromSelector(Selektor) hatPräfix:@"__rct_export__"]) { IMP imp = method_getImplementation(Methode); NSArray *Einträge = ((NSArray *(*)(id, SEL))imp)(_moduleClass, Selektor); //... [moduleMethods addObject:/* Objekt, das die Methode darstellt */]; } } Einrichten des JavaScript-ExecutorsDer JS-Executor verfügt über eine -setUp-Methode, mit der er komplexere Aufgaben ausführen kann, z. B. JS-Code in einem Hintergrundthread initialisieren kann. Dies spart auch einiges an Arbeit, da nur der aktive Executor den Aufruf der setUp-Methode erhält, nicht alle Executors: JSGlobalContextRef ctx = JSGlobalContextCreate(NULL); _context = [[RCTJavaScriptContext alloc] initWithJSContext:ctx]; Einfügen einer JSON-KonfigurationDie JSON-Konfiguration enthält nur unser Modul, zum Beispiel: Diese Konfigurationsinformationen werden als globale Variable in der virtuellen JavaScript-Maschine gespeichert, sodass die JS-Seitenbrücke bei ihrer Initialisierung diese Informationen zum Erstellen von Modulen verwenden kann. JavaScript-Code wird geladenDies ist ziemlich unkompliziert und erfordert lediglich das Laden des Quellcodes von dem von Ihnen angegebenen Anbieter, normalerweise vom Packager während der Entwicklung und von der Festplatte während der Produktion. Ausführen von JavaScript-CodeSobald alles bereit ist, können wir den Quellcode der Anwendung in die virtuelle JS-Maschine laden, den Code kopieren, analysieren und ausführen. Alle CommonJS-Module müssen bei der ersten Ausführung registriert werden und die Eintragsdatei wird benötigt. JSValueRef jsError = NULL; JSStringRef execJSString = JSStringCreateWithCFString((__bridge CFStringRef)script); JSStringRef jsURL = JSStringCreateWithCFString((__bridge CFStringRef)sourceURL.absoluteString); JSValueRef-Ergebnis = JSEvaluateScript(strongSelf->_context.ctx, execJSString, NULL, jsURL, 0, &jsError); JSStringRelease(jsURL); JSStringRelease(execJSString); Module in JavaScriptAuf der JS-Seite können wir jetzt das Modul, das aus den vorherigen JSON-Konfigurationsinformationen besteht, über die NativeModules von react-native abrufen: Die Funktionsweise besteht darin, dass beim Aufruf einer Methode diese zusammen mit dem Namen des Moduls, dem Namen der Methode und allen Parametern in eine Warteschlange gestellt wird. Am Ende der JavaScript-Ausführung wird diese Warteschlange dem nativen Modul zur Ausführung übergeben. AnrufzyklusWenn wir das Modul nun mit dem obigen Code aufrufen, sieht es folgendermaßen aus: Der Aufruf muss nativ beginnen, und nativ ruft JS auf (dieses Bild erfasst nur einen bestimmten Moment, in dem JS ausgeführt wird). Während des Ausführungsprozesses ruft JS die Methode von NativeModules auf und stellt diesen Aufruf in die Warteschlange, da dieser Aufruf auf der nativen Seite ausgeführt werden muss. Wenn die Ausführung von JS abgeschlossen ist, durchläuft das native Modul alle in die Warteschlange gestellten Aufrufe. Wenn die Ausführung abgeschlossen ist, ruft es über die Brücke zurück (ein natives Modul kann enqueueJSCall:args: über die _bridge-Instanz aufrufen), um erneut JS aufzurufen. (Wenn Sie das Projekt verfolgt haben, gab es früher auch eine Anrufwarteschlange von native->JS, die bei jedem vSYNC versendet wurde, aber das wurde entfernt, um die Startzeit zu verbessern) ParametertypNative-zu-JS-Aufrufe sind einfach, die Parameter werden als NSArray übergeben, das wir als JSON-Daten kodieren, aber für JS-zu-Native-Aufrufe brauchen wir den nativen Typ, dafür prüfen wir die Basistypen (Ints, Floats, Chars...), aber wie oben erwähnt, bekommen wir für kein Objekt (keine Struktur) zur Laufzeit genügend Informationen von NSMthodSignature, also speichern wir den Typ als Zeichenfolge. Wir verwenden reguläre Ausdrücke, um den Typ aus der Methodensignatur zu extrahieren, und verwenden die Klasse RCTConvert, um das Objekt tatsächlich zu konvertieren. Standardmäßig stellt sie Methoden für jeden Typ bereit und versucht, den JSON-Eingang in den erforderlichen Typ zu konvertieren. Sofern es sich nicht um eine Struktur handelt, verwenden wir objc_msgSend, um die Methode dynamisch aufzurufen, da es auf arm64 keine Version von objc_msgSend_stret gibt. Daher verwenden wir NSInvocation. Nachdem wir alle Parameter konvertiert haben, verwenden wir eine weitere NSInvocation, um das Zielmodul und die Zielmethode aufzurufen. Beispiel: // Wenn Sie die folgende Methode in einem bestimmten Modul hätten, z. B. „MyModule“ RCT_EXPORT_METHOD(MethodeMitArray:(NSArray *) Größe:(CGRect)Größe) {} // Und habe es von JS aus aufgerufen, etwa: erfordern('NativeModules').MeinModul.Methode(['a', 1], { x: 0, y: 0, Breite: 200, Höhe: 100 }); // Die an das native System gesendete JS-Warteschlange würde dann wie folgt aussehen: // ** Denken Sie daran, dass es sich um eine Warteschlange von Anrufen handelt, daher sind alle Felder Arrays ** @[ @[ @0 ], // Modul-IDs @[ @1 ], // Methoden-IDs @[ // Argumente @[ @[@"ein", @1], @{ @"x": @0, @"y": @0, @"Breite": @200, @"Höhe": @100 } ] ] ]; // Dies würde sich in folgende Aufrufe umwandeln (Pseudocode) NSInvocation-Aufruf Aufruf[Argumente][0] = GetModuleForId(@0) Aufruf[Argumente][1] = GetMethodForId(@1) Aufruf[Argumente][2] = obj_msgSend(RCTConvert, NSArray, @[@"a", @1]) Aufruf[Argumente][3] = NSInvocation(RCTConvert, CGRect, @{ @"x": @0, ... }) Anruf() ThemenWie oben erwähnt, verfügt jedes Modul standardmäßig über eine einzelne GCD-Warteschlange, es sei denn, es gibt durch Implementierung der Methode -methodQueue oder Zusammenführen der Eigenschaft methodQueue mit einer gültigen Warteschlange an, welche Warteschlange ausgeführt werden soll. Die Ausnahme sind ViewManager* (die RCTViewManager erweitern), die standardmäßig Shadow Queue verwenden, und das spezielle Ziel RCTJSThread ist nur ein Platzhalter, da es ein Thread und keine Warteschlange ist. (Tatsächlich sind View-Manager keine wirkliche Ausnahme, da die Basisklasse die Shadow Queue ausdrücklich als Zielwarteschlange angibt.) Die aktuellen Thread-Regeln lauten wie folgt:
Wenn ein Stapel von JS-Aufrufen eingeht, werden diese Aufrufe nach Zielwarteschlange gruppiert und parallel aufgerufen: // gruppiere „Anrufe“ nach „Warteschlange“ in „Buckets“ für (ID-Warteschlange in Buckets) { dispatch_block_t block = ^{ NSOrderedSet *Aufrufe = [Buckets Objekt für Schlüssel:Warteschlange]; für (NSNumber *indexObj in Anrufen) { // Tatsächlich anrufen } }; wenn (Warteschlange == RCTJSThread) { [_javaScriptExecutor executeBlockOnJavaScriptQueue:block]; } sonst wenn (Warteschlange) { dispatch_async(Warteschlange, Block); } } AbschlussDas war unser ausführlicherer Überblick über die Funktionsweise von React Native Bridging. Ich hoffe, dies hilft Leuten, die komplexere Module erstellen oder zum Kern-Framework beitragen möchten. Dies ist das Ende dieses Artikels über ein vertieftes Verständnis der Kernprinzipien von React Native (React Native Bridge). Weitere Inhalte zu den Prinzipien von React Native finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird! Das könnte Sie auch interessieren:
|
<<: Detailliertes Tutorial zur Installation von MySQL auf CentOS 6.9
>>: Zusammenfassung der unbekannten Verwendung von "!" in Linux
Vorwort Jede gute Angewohnheit ist ein Schatz. Di...
Inhaltsverzeichnis Benutzerdefinierte Vue-Direkti...
Durch einen Rechtsklick auf die Quelldatei wird fo...
In diesem Artikelbeispiel wird der spezifische Co...
Auf vielen Websites wird im Eingabefeld Hinweiste...
Lösen Sie das Problem der verstümmelten chinesisc...
Wie installiere ich PHP7 unter Linux? 1. Installi...
Laden Sie das Tutorial zum Paket mysql-connector-...
Inhaltsverzeichnis Schmutzige Seiten (Speichersei...
Frage. Im mobilen Shopping-Mall-System sehen wir ...
Das Temperament einer Web-Frontend-Website ist ein...
Es gibt viele Tags in XHTML, aber nur wenige werd...
Um die Unterstreichung eines Hyperlinks zu entfern...
Inhaltsverzeichnis 1. Animierter Weihnachtsbaum, ...
Inhaltsverzeichnis Probleme, die Redux Toolkit lö...