1、iOS與RN的互調
相對於Android來說,iOS的兩端互調比較簡單。先看iOS裏 ShowAlert的配置:
// .h
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
NS_ASSUME_NONNULL_BEGIN
@interface ShowAlert : RCTEventEmitter<RCTBridgeModule>
@end
NS_ASSUME_NONNULL_END
// .m
#import "ShowAlert.h"
@interface ShowAlert ()
@property (nonatomic, assign) BOOL hasListener;
@end
static NSString * const EVENT_NAME = @"AlertDismiss";
@implementation ShowAlert
// 如果 RCT_EXPORT_MODULE 裏有傳參, 則按照傳參作爲模塊名, 否則是類名
RCT_EXPORT_MODULE(Alert)
// 原生調用RN
- (NSArray<NSString *> *)supportedEvents {
return @[EVENT_NAME];
}
// 在添加第一個監聽函數時觸發
- (void)startObserving {
self.hasListener = YES;
}
// 當該模塊的最後一個監聽者被移除, 或者釋放的時候 調用
- (void)stopObserving {
self.hasListener = NO;
}
- (void)sendToRNWithInfo:(NSString *)info {
// 只有在有監聽的情況下才發射事件, 防止資源浪費和警告
if (self.hasListener) {
[self sendEventWithName:EVENT_NAME body:info];
}
}
- (void)in_showAlert:(NSString *)title content:(NSString *) content done:(RCTResponseSenderBlock)done {
UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:title message:content preferredStyle: UIAlertControllerStyleAlert];
UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[self sendToRNWithInfo: @"確定"];
}];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[self sendToRNWithInfo: @"取消"];
}];
[alertVC addAction:defaultAction];
[alertVC addAction:cancelAction];
[[UIApplication sharedApplication].delegate.window.rootViewController presentViewController:alertVC animated:true completion:^{
!done ?: done(@[@"已經展示了"]);
}];
}
// 暴露的方法名
RCT_EXPORT_METHOD(showAlert:(NSString *)title content:(NSString *) content done:(RCTResponseSenderBlock)done) {
[self in_showAlert:title content:content done:done];
}
// 導出的常量, 取值方式: 模塊.key
- (NSDictionary *)constantsToExport {
return @{
@"one": @"大寫的一",
@"two": @"大寫的二"
};
}
// 重寫constantsToExport, 必須重寫此方法
+ (BOOL)requiresMainQueueSetup {
return true;
}
// 涉及UI的, 表明初始化的線程, 默認爲background線程
- (dispatch_queue_t)methodQueue{
return dispatch_get_main_queue();
}
@end
在 RN 裏使用:
import { Button } from '@ant-design/react-native'
import { memo, useEffect } from 'react'
import { NativeEventEmitter, NativeModules, View } from 'react-native'
// 獲取模塊名
const { Alert } = NativeModules
// 原生事件發射器
const alertEmitter = new NativeEventEmitter(Alert)
export default memo(function HomePage() {
useEffect(() => {
// 添加監聽, 用於被原生模塊調用
const subscription = alertEmitter.addListener('AlertDismiss', info => {
console.log('選擇的狀態info :>> ', info)
})
return () => {
// 在組件卸載的時候移除監聽
subscription.remove()
}
}, [])
return (
<View>
<Button
onPress={() => {
// RN調用原生方法
NativeModules.Alert.showAlert('這是標題', '這是內容', info => {
console.log('error, events :>> ', info)
})
}}>
調用原生模塊
</Button>
</View>
)
})
2、Android與RN的互調
a. 編寫Module CalendarModule
package com.zxmall;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import java.util.HashMap;
import java.util.Map;
public class CalendarModule extends ReactContextBaseJavaModule {
CalendarModule(ReactApplicationContext context) {
super(context);
}
/**
* 模塊名稱
*/
@NonNull
@Override
public String getName() {
return "CalendarModule";
}
/**
* 導出常量, 取值: 模塊名.key 的方式
* @return 暴露給RN的常量
*/
@Nullable
@Override
public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>();
constants.put("one", "大寫的一");
constants.put("two", "大寫的二");
return constants;
}
/**
* 給RN調用的原生方法
* @param name RN傳過來的值
* @param location RN傳過來的值
* @param cb 回調給RN
*/
@ReactMethod
public void createCalendarEvent(String name, String location, Callback cb) {
Log.i("calendar", name + location);
cb.invoke("已經完成了打印");
}
}
b. 編寫ReactPackage:CalendarPackage
在React native中註冊原生模塊: 將原生模塊添加到一個ReactPackage中,並在React native中註冊這個ReactPackage。
在初始化過程中,React Native將遍歷所有包,併爲每個ReactPackage註冊其中的每個原生模塊。
package com.zxmall;
import androidx.annotation.NonNull;
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 CalendarPackage implements ReactPackage {
@NonNull
@Override
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactApplicationContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new CalendarModule(reactApplicationContext));
return modules;
}
@NonNull
@Override
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactApplicationContext) {
return Collections.emptyList();
}
}
c. 在 MainApplication 裏註冊。
在 RN 裏使用:
import { Button } from '@ant-design/react-native'
import { memo } from 'react'
import { NativeModules, View } from 'react-native'
export default memo(function HomePage() {
return (
<View>
<Button
onPress={() => {
NativeModules.CalendarModule.createCalendarEvent(
'這是標題',
'這是地點',
info => {
console.log('安卓原生回調信息 :>> ', info)
}
)
}}>
調用原生模塊
</Button>
</View>
)
})