- 原文地址: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 類。java
文本是此係列的第一部分,第二部分能夠在 Native Bridge of UI component 中找到 這裏。react
代碼能夠在這裏找到 -> github.com/nalwayaabhi…android
爲了更好地理解 Native Module,咱們將使用 react-native CLI 建立一個簡單的 LightApp 示例。ios
$ react-native init LightApp
$ cd LightApp
複製代碼
接下來,咱們將在 Swift 和 Java 中建立一個 Bulb
類,稍後將在 React 組件中使用它。這是一個跨平臺的示例,相同的 React 代碼將同時適用於iOS和Android。git
如今咱們已經建立了項目的基本框架,接下來咱們將本文分爲兩部分:github
第一節 — 與原生 iOS 通訊swift
第二節 — 與原生 Android 通訊後端
在本節中,咱們將重點關注 iOS,瞭解如何在 Swift/Objective C 和 React 組件間創建橋樑。有如下三個步驟:react-native
步驟 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 對象類型:
如今讓咱們更新 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 運行的全部線程:
除非另有說明,不然每一個原生模塊都有本身的 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
請記住從新編譯代碼而不是刷新,由於咱們更改了原生代碼。
在本節中,咱們將使用與 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、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。