iOS端如何操作
// Test.h文件
#import
#import
// 遵循RCTBridgeModule
@interface Test : NSObject
@end
// Test.m文件
#import "Test.h"
@implementation Test
/// 导出一个模块,括号内是可选的,若不填,默认为类名
RCT_EXPORT_MODULE(Test);
/// 导出一个普通的异步方法,
RCT_EXPORT_METHOD(test:(NSString *)name) {
NSLog(@"%@",name);
}
/// 导出一个支持Promise的异步方法
RCT_EXPORT_METHOD(testPromise:(NSString *)name
resolve:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
resolve(@"success");
}
/// 导出一个同步方法
RCT_EXPORT_SYNCHRONOUS_TYPED_METHOD(NSString *, testSync:(NSString *)name) {
return [[NSString alloc]initWithFormat:@"hello %@", name];
}
/// 导出常量供RN使用
- (NSDictionary *)constantsToExport {
return @{@"testConstant": @"constant"};
}
@end
复制代码
RN端如何使用?
/// 导入模块
import {NativeModules} from 'react-native';
// 调用异步的方法
NativeModules.Test.test("sync");
// 使用await调用支持Promise的方法
let res = await NativeModules.Test.testPromise('promise');
console.log(res)
/// 调用同步的方法
let syncRes = NativeModules.Test.testSync('sync');
console.log(syncRes);
why?
如果你能对上面的问题都清楚,那么下面的内容对你应该没什么帮助。
**
**
#define RCT_EXPORT_MODULE(js_name)
RCT_EXTERN void RCTRegisterModule(Class);
+(NSString *)moduleName
{
return @ #js_name;
}
+(void)load
{
RCTRegisterModule(self);
}
根据上面的代码可以看出,RCT_EXPORT_MODULE一共做了两件事,
/// 导出一个普通的异步方法,
RCT_EXPORT_METHOD(test:(NSString *)name) {
NSLog(@"%@",name);
}
/// 完全展开之后
-(void)test:(NSString *)name ; {
NSLog(@"%@",name);
}
+(const RCTMethodInfo *)__rct_export__(__LINE__, __COUNTER__这里是当前的行数加上预编译的次数){
static RCTMethodInfo config = {"", "test:(NSString *)name", NO};
return &config;
}
/// 导出一个支持Promise的异步方法
RCT_EXPORT_METHOD(testPromise:(NSString *)name
resolve:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
resolve(@"success");
}
/// 完全展开之后
-(void)testPromise:(NSString *)name
resolve:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject ; {
resolve(@"success");
}
+(const RCTMethodInfo *)__rct_export__(__LINE__, __COUNTER__这里是当前的行数加上预编译的次数){
static RCTMethodInfo config = {"", "testPromise:(NSString *)name resolve:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject", NO};
return &config;
}
/// 导出一个同步方法
RCT_EXPORT_SYNCHRONOUS_TYPED_METHOD(NSString *, testSync:(NSString *)name) {
return [[NSString alloc]initWithFormat:@"hello %@", name];
}
/// 完全展开之后
-(NSString *)testSync:(NSString *)name ; {
return [[NSString alloc]initWithFormat:@"hello %@", name];
}
+(const RCTMethodInfo *)__rct_export__(__LINE__, __COUNTER__这里是当前的行数加上预编译的次数){
static RCTMethodInfo config = {"", "testSync:(NSString *)name", YES};
return &config;
}
通过上面的代码可以看出,
无论RCT_EXPORT_METHOD还是RCT_EXPORT_SYNCHRONOUS_TYPED_METHOD做的事情都一样,都是生成了一个对象方法,方法的名字就是括号内的参数,方法的实现就是宏定义后面跟着的{}里的实现,并且同时生成了一个以`__rct_export__`开头的类方法,里面返回了一个静态变量的结构体的地址,定义如下
typedef struct RCTMethodInfo {
const char *const jsName;
const char *const objcName;
const BOOL isSync;
} RCTMethodInfo;
而RCT_EXPORT_METHOD和RCT_EXPORT_SYNCHRONOUS_TYPED_METHOD的区别是后者生成的方法是带返回值的,而前者固定为void,后者生成的结构体里的信息最后一个字段为YES。
小结:上面一堆宏定义一共做了4件事
下面这段代码,会在Bridge初始化的过程中调用,在js执行环境的gloabl上添加了三个全局变量,nativeModuleProxy,nativeFlushQueueImmediate,nativeCallSyncHook后面两个是函数。后面再详细介绍
runtime_->global().setProperty(
*runtime_,
"nativeModuleProxy",
Object::createFromHostObject(*runtime_, std::make_shared(nativeModules_)));
runtime_->global().setProperty(
*runtime_,
"nativeFlushQueueImmediate",
Function::createFromHostFunction(
*runtime_,
PropNameID::forAscii(*runtime_, "nativeFlushQueueImmediate"),
1,
[this](
jsi::Runtime &,
const jsi::Value &,
const jsi::Value *args,
size_t count) {
if (count != 1) {
throw std::invalid_argument(
"nativeFlushQueueImmediate arg count must be 1");
}
callNativeModules(args[0], false);
return Value::undefined();
}));
runtime_->global().setProperty(
*runtime_,
"nativeCallSyncHook",
Function::createFromHostFunction(
*runtime_,
PropNameID::forAscii(*runtime_, "nativeCallSyncHook"),
1,
[this](
jsi::Runtime &,
const jsi::Value &,
const jsi::Value *args,
size_t count) { return nativeCallSyncHook(args, count); }));
先来看NativeModules,NativeModules是从react-native/Libraries/BatchedBridge/NativeModules.js文件中导出的就是上面在原生中创建的全局变量nativeModuleProxy。
NativeModules.Test 最终会调用JSINativeModules::getModule的方法,这个方法主要做了这些。
下面代码中的module就是NativeModules.Test这个属性的值,在遍历所有方法的过程,用方法名字为属性名字,genMethod(moduleID, methodID, methodType);的结果为值。genMethod的返回值也是一个函数
function genModule(
config: ?ModuleConfig,
moduleID: number,
){
if (!config) {
return null;
}
const [moduleName, constants, methods, promiseMethods, syncMethods] = config;
const module = {};
methods &&
methods.forEach((methodName, methodID) => {
const isPromise =
(promiseMethods && arrayContains(promiseMethods, methodID)) || false;
const isSync =
(syncMethods && arrayContains(syncMethods, methodID)) || false;
const methodType = isPromise ? 'promise' : isSync ? 'sync' : 'async';
module[methodName] = genMethod(moduleID, methodID, methodType);
});
Object.assign(module, constants);
if (module.getConstants == null) {
module.getConstants = () => constants || Object.freeze({});
}
return {name: moduleName, module};
}
genMethod实现是方法类型是则调用BatchedBridge.callNativeSyncHook的方法,如果是异步的方法则调用BatchedBridge.enqueueNativeCall,如果是promise的,则用Promise做一层封装,再调用了BatchedBridge.enqueueNativeCall
function genMethod(moduleID: number, methodID: number, type: MethodType) {
let fn = null;
if (type === 'promise') {
fn = function promiseMethodWrapper(...args: Array) {
const enqueueingFrameError: ExtendedError = new Error();
return new Promise((resolve, reject) => {
BatchedBridge.enqueueNativeCall(
moduleID,
methodID,
args,
data => resolve(data),
errorData =>
reject(
updateErrorWithErrorData(
(errorData: $FlowFixMe),
enqueueingFrameError,
),
),
);
});
};
} else {
fn = function nonPromiseMethodWrapper(...args: Array) {
const lastArg = args.length > 0 ? args[args.length - 1] : null;
const secondLastArg = args.length > 1 ? args[args.length - 2] : null;
const hasSuccessCallback = typeof lastArg === 'function';
const hasErrorCallback = typeof secondLastArg === 'function';
hasErrorCallback &&
invariant(
hasSuccessCallback,
'Cannot have a non-function arg after a function arg.',
);
// $FlowFixMe[incompatible-type]
const onSuccess: ?(mixed) => void = hasSuccessCallback ? lastArg : null;
// $FlowFixMe[incompatible-type]
const onFail: ?(mixed) => void = hasErrorCallback ? secondLastArg : null;
const callbackCount = hasSuccessCallback + hasErrorCallback;
const newArgs = args.slice(0, args.length - callbackCount);
if (type === 'sync') {
return BatchedBridge.callNativeSyncHook(
moduleID,
methodID,
newArgs,
onFail,
onSuccess,
);
} else {
BatchedBridge.enqueueNativeCall(
moduleID,
methodID,
newArgs,
onFail,
onSuccess,
);
}
};
}
fn.type = type;
return fn;
}
写在最后的话
在写这篇文章之前,这些代码已经翻过N遍了,但是真正也起来还是比较乱。写的真累…自己看明白和能写出来真不是一回事
作者:李坤
链接:https://juejin.cn/post/6965082621801955364
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/1059/viewspace-2797533/ ,如需转载,请注明出处,否则将追究法律责任。