- 原文地址:React Native Bridge for iOS and Android
- 原文作者:Abhishek Nalwaya
- 譯文出自:掘金翻譯計劃
- 本文永久連結:github.com/xitu/gold-m…
- 譯者:MeandNi
- 校對者:lsvih
React Native 流行的最大原因之一是我們可以在 Native 語言和 JavaScript 程式碼之間建立橋樑。這意味著我們可以複用在 iOS 和 Android 中建立的所有可重用庫。
要建立一個商業級的應用程式,您需要使用 Native Bridge。React Native 可以同時在 iOS 和 Android 上執行,但關於它如何跨平臺的文章教程非常少。在本文中,我們將建立一個 Native Bridge,以便從 JavaScript 訪問 Swift 和 Java 類。
文字是此係列的第一部分,第二部分可以在 Native Bridge of UI component 中找到 這裡。
程式碼可以在這裡找到 -> github.com/nalwayaabhi…
建立一個 LightApp(手電筒)
為了更好地理解 Native Module,我們將使用 react-native CLI 建立一個簡單的 LightApp 示例。
$ react-native init LightApp
$ cd LightApp
複製程式碼
接下來,我們將在 Swift 和 Java 中建立一個 Bulb
類,稍後將在 React 元件中使用它。這是一個跨平臺的示例,相同的 React 程式碼將同時適用於iOS和Android。
現在我們已經建立了專案的基本框架,接下來我們將本文分為兩部分:
第一節 — 與原生 iOS 通訊
第二節 — 與原生 Android 通訊
第一節 — 與原生 iOS 通訊
在本節中,我們將重點關注 iOS,瞭解如何在 Swift/Objective C 和 React 元件間建立橋樑。有以下三個步驟:
步驟 1) 建立一個 Bulb 類 並且完整初步通訊
步驟 2) 理解 GCD Queue 並且解決出現的警告
步驟 3) 從 Swift 和 Callbacks 訪問 JavaScript 中的變數
步驟 1) 建立一個 Bulb 類 並且完成初步通訊
首先,我們將在 swift 中建立一個 Bulb 類,它將具有一個靜態類變數 isOn
和一些其他函式。然後我們將從 Javascript 訪問這個 swift 類。讓我們首先在 ios 資料夾中開啟 LightApp.xcodeproj 檔案。此時 Xcode 應該會被開啟。
在 Xcode 中開啟專案後,建立一個新的 Swift檔案 Bulb.swift,如下所示:
我們還要點選 Create Bridging Header,建立一個檔案 LightApp-Bridging-Header.h
它將有助於 Swift 和 Objective C 程式碼之間的通訊。請記住,在專案中,我們只有一個 Bridge Header 檔案。因此,如果我們新增新檔案,我們可以重用此檔案。
將以下程式碼加入 -Bridging-Header.h
檔案:
#import "React/RCTBridgeModule.h"
複製程式碼
RCTBridgeModul 將提供一個介面,用於註冊 Bridge 模組。
接下來將以下程式碼輸入 Bulb.swift
:
import Foundation
@objc(Bulb)
class Bulb: NSObject {
@objc
static var isOn = false
@objc
func turnOn() {
Bulb.isOn = true
print("Bulb is now ON")
}
}
複製程式碼
我們建立了 Bulb
類,它繼承自 NSObject。大多數 Objective-C 類的根類是 NSObject,子類從該類繼承執行時系統的基本介面,因此它們有與 Objective-C 物件相同的能力。我們在函式和類之前使用了 @objc,這將使那個類,方法或物件可用於 Objective C。
@objc 註解使您的 Swift API 在 Objective-C 和 Objective-C 執行時可用。
現在選擇 File -> New -> File 建立一個新檔案,然後選擇 Objective-C 檔案,然後將該檔案命名為 Bulb.m 並新增以下程式碼:
#import "React/RCTBridgeModule.h"
@interface RCT_EXTERN_MODULE(Bulb, NSObject)
RCT_EXTERN_METHOD(turnOn)
@end
複製程式碼
除非顯式地指定,否則 React Native 不會將任何 Bulb 中的函式暴露給 React JavaScript。為此,我們使用了 RCT_EXPORT_METHOD() 巨集。所以我們已經暴露了 Bulb 類和 turnOn 函式給了我們的 Javascript 程式碼。由於 Swift 物件被轉換為了 Javascript 物件,因此其中一定存在一種對應關係。RCT_EXPORT_METHOD 支援所有標準 JSON 物件型別:
- NSString 對應 string
- NSInteger、float、double、CGFloat、NSNumber 對應 number
- BOOL 對應 boolean
- NSArray 對應 array
- NSDictionary 對應 包含此列表中的字串鍵和任何型別的值的物件
- RCTResponseSenderBlock 對應 function
現在讓我們更新 JavaScript 程式碼並從我們的 React 元件訪問這個 Bulb 類。為此,請開啟 App.js 並更新為以下程式碼:
import React, {Component} from 'react';
import {StyleSheet, Text, View, NativeModules, Button} from 'react-native';
export default class App extends Component{
turnOn = () => {
NativeModules.Bulb.turnOn();
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to Light App!!</Text>
<Button
onPress={this.turnOn}
title="Turn ON "
color="#FF6347" />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
});
複製程式碼
現在執行iOS模擬器:
現在開啟 Xcode 控制檯檢視日誌,我們可以看到從 JavaScript 程式碼呼叫 Swift turnOn 方法。(因為我們已經看到了方法中執行的日誌)
步驟 2) 理解 GCD Queue並且解決出現的警告
現在讓我們修復模擬器底部和瀏覽器控制檯中顯示的警告:
Bulb 模組需要主佇列設定,因為它覆蓋 init
但 沒有實現 requiresMainQueueSetup
。在以後的版本中,React Native 將預設初始化後臺執行緒上的所有原生模組,除非明確選擇不需要。
為了更好地理解,讓我們瞭解 React Native 執行的所有執行緒:
- Main thread:UI 渲染執行的執行緒
- Shadow queue:佈局發生的地方
- JavaScript thread:JS 程式碼實際執行的地方
除非另有說明,否則每個原生模組都有自己的 GCD 佇列。 現在,由於這個原生模組將在不同的執行緒上執行,並且我們的主執行緒依賴於它,它會顯示此警告。 要使此程式碼在 MainQueue 上執行,請開啟 Bulb.swift 並新增此函式。
@objc
static func requiresMainQueueSetup() -> Bool {
return true
}
複製程式碼
您可以明確提及 return false 以讓它在單獨的執行緒中執行。
步驟 3) 從Swift和Callbacks訪問JavaScript中的變數
現在讓我們將 Bulb 的開關(ON 或 OFF)值新增到 React 螢幕。為此,我們將 getStatus 函式新增到 Bulb.swift 並從 JavaScript 程式碼呼叫該方法。 我們將建立此方法作為回撥。
React Native 橋是非同步的,因此將結果傳遞給 JavaScript 的唯一方法是使用回撥或觸發事件
讓我們用粗體更新 Bulb.swift 中的程式碼:
@objc(Bulb)
class Bulb: NSObject {
@objc
static var isOn = false
@objc
func turnOn() {
Bulb.isOn = true
print("Bulb is now ON")
}
@objc
func turnOff() {
Bulb.isOn = false
print("Bulb is now OFF")
}
@objc
func getStatus(_ callback: RCTResponseSenderBlock) {
callback([NSNull(), Bulb.isOn])
}
@objc
static func requiresMainQueueSetup() -> Bool {
return true
}
}
複製程式碼
getStatus() 方法接收一個我們將從您的 JavaScript 程式碼傳遞的回撥引數。我們用值陣列呼叫了回撥函式,這些函式將暴露在 JavaScript 中。我們已經將 NSNull() 作為第一個元素傳遞,我們將其視為回撥中的錯誤。
我們需要將這個 Swift 方法暴露給 JavaScript,所以新增下方的粗體行程式碼到 Bulb.m 中:
@interface RCT_EXTERN_MODULE(Bulb, NSObject)
RCT_EXTERN_METHOD(turnOn)
RCT_EXTERN_METHOD(turnOff)
RCT_EXTERN_METHOD(getStatus: (RCTResponseSenderBlock)callback)
@end
複製程式碼
我們已將 (RCTResponseSenderBlock)callback 暴露為函式 getStatus 的引數
然後最後更新React程式碼:
import React, {Component} from 'react';
import {StyleSheet, Text, View, NativeModules, Button} from 'react-native';
export default class App extends Component{
constructor(props) {
super(props);
this.state = { isOn: false };
this.updateStatus();
}
turnOn = () => {
NativeModules.Bulb.turnOn();
this.updateStatus()
}
turnOff = () => {
NativeModules.Bulb.turnOff();
this.updateStatus()
}
updateStatus = () => {
NativeModules.Bulb.getStatus( (error, isOn)=>{
this.setState({ isOn: isOn});
})
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to Light App!!</Text>
<Text> Bulb is {this.state.isOn ? "ON": "OFF"}</Text>
{!this.state.isOn ? <Button
onPress={this.turnOn}
title="Turn ON "
color="#FF6347"
/> :
<Button
onPress={this.turnOff}
title="Turn OFF "
color="#FF6347"
/> }
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
});
複製程式碼
重新變異程式碼並執行應用程式,您可以看到 Bulb Status 的值,當您單擊 Turn ON 時,它將顯示 Bulb 為 ON
請記住重新編譯程式碼而不是重新整理,因為我們更改了原生程式碼。
Section 2 — Native Bridge in Android
在本節中,我們將使用與 iOS 相同的 Javascript 程式碼,它同樣可以應用在 Android 中。這次我們將在 Java 中建立 Bulb 類並將相同的函式 turnOn, TurnOff 和 getStatus 暴露給 Javascript。
開啟 Android Studio 並單擊 開啟現有的 Android Studio 專案,然後在 LightApp 中選擇 android 資料夾。下載所有 gradle 依賴項後,建立一個 Java 類 Bulb.java,如下所示:
並將 Bulb.java 中程式碼更新為:
package com.lightapp;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;
public class Bulb extends ReactContextBaseJavaModule {
private static Boolean isOn = false;
public Bulb(ReactApplicationContext reactContext) {
super(reactContext);
}
@ReactMethod
public void getStatus(
Callback successCallback) {
successCallback.invoke(null, isOn);
}
@ReactMethod
public void turnOn() {
isOn = true;
System.out.println("Bulb is turn ON");
}
@ReactMethod
public void turnOff() {
isOn = false;
System.out.println("Bulb is turn OFF");
}
@Override
public String getName() {
return "Bulb";
}
}
複製程式碼
我們建立了一個 Bulb Java 類,它繼承自 ReactContextBaseJavaModule 。 ReactContextBaseJavaModule 要求一定要實現名一個為 getName 的函式。此方法的作用是返回在 JavaScript 中表示此類的 NativeModule 的字串名稱。所以在這裡我們將呼叫 Bulb ,以便我們可以通過 JavaScript 中的 React.NativeModules.Bulb 來訪問它。我們可以使用其它不同的名稱代替 Bulb。
並非所有函式都顯式地暴露給 Javascript,要向 JavaScript 公開函式,必須使用 @ReactMethod 註解 Java 方法。橋接方法的返回型別始終為 void。
我們還建立了一個 getStatu 函式,它具有引數作為回撥,它返回一個 callback 並傳遞靜態變數 isOn 的值。
下一步是註冊模組,如果模組未註冊,則無法從 JavaScript 獲得。通過單擊選單"檔案” ->“新建” -> “Java 類”並將檔名設定為 BulbPackage 來建立檔案,然後單擊“確定”。然後將以下程式碼新增到 BulbPackage.java
package com.lightapp;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class BulbPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new Bulb(reactContext));
return modules;
}
}
複製程式碼
我們需要覆蓋 createNativeModules 函式並將 Bulb 物件新增到 modules 陣列中。如果這裡沒有新增,那麼它將無法在 JavaScript 中使用。
需要在 MainApplication.java 檔案的 getPackages 方法中提供 BulbPackage 包。此檔案存在於 react-native 應用程式目錄中的 android 資料夾下。在 android/app/src/main/java/com/LightApp/MainApplication.java 中更新以下程式碼
public class MainApplication extends Application implements ReactApplication {
...
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new BulbPackage()
);
}
....
}
複製程式碼
我們不需要更改在 iOS 中編寫的任何 JavaScript 程式碼,因為我們已經暴露了相同的類名和函式。如果您已跳過 iOS 部分,則需要從 App.js 複製 React Javascript 程式碼。
現在通過 Android Studio 或 react-native run-android 執行 App:
哇唔!我們可以在螢幕上看到 Bulb 狀態,並可以從按鈕切換 ON 或 OFF。最棒的是我們建立了一個跨平臺的應用。
如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。