dart ⇋ iOS Native ⇋ js
-
dart 异步调用 native
-
dart 同步调用 native
dart 异步调用 native
1、dart 调用 ios native
使用 BasicMessageChannel 与iOS native 通讯
dart 实现: BasicMessageChannel
final String COMMON_MESSAGE_CHANNEL = 'com.wuba.fair/common_message_channel';
_commonChannel ??=
BasicMessageChannel<String?>(COMMON_MESSAGE_CHANNEL, StringCodec());
Future<String?> sendCommonMessage(dynamic msg) async {
return _commonChannel!.send(msg);
}
iOS 实现 FlutterBasicMessageChannel
NSString * **const** FairMessageChannelExecuteID = @"com.wuba.fair/common_message_channel";
// 定义对象
self.flutterBasicMessageChannel = [[FlutterBasicMessageChannel alloc] initWithName:FairMessageChannelExecuteID
binaryMessenger:self.binaryMessenger
codec:[FlutterStringCodec sharedInstance]];
// 绑定方法
[self.flutterBasicMessageChannel setMessageHandler:^(id message, FlutterReply callback) {
FairStrongObject(strongSelf, weakSelf);
FairLog(@"%@", message);
if (strongSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(executeJSFunctionAsync:params:callback:)]) {
NSArray *params = message && [message isKindOfClass:[NSString class]] ? @[message] : @[];
[strongSelf.delegate executeJSFunctionAsync:FairExecuteJSFunction params:params callback:^(id result, NSError *error) {
FairLog(@"%@", result);
JSValue *value = result;
if (value && [value isKindOfClass:[JSValue class]]) {
NSString *str = value.toString;
FairLog(@"%@", str);
if (![str isEqualToString:@"undefined"] && FAIR_IS_NOT_EMPTY_STRING(str)) {
callback(str);
}
}
}];
}
}];
2、ios native 调用执行js代码
JavaScriptCore:JavaScriptCore是iOS上的JavaScript引擎,可以在Objective-C和JavaScript之间进行互操作。通过使用JavaScriptCore,可以在运行时执行动态的JavaScript代码,从而实现逻辑的动态化。
创建JSContext对象,执行JavaScript代码,并通过JSValue对象进行数据交换。通过JavaScriptCore,也可以将Native对象公开给JavaScript,并从JavaScript中调用Native方法。
跟踪一下上面的 invokeJSFunctionOnJSThread 方法: 本质上调用的是JSValue 对象call方法, 用来执行 js 逻辑代码
- (JSValue *)invokeJSFunctionOnJSThread:(NSString *)functionName params:(NSArray *)params callback:(FairCallback)callback
{
JSValue *jsValue = self.context[functionName];
JSValue *value = [jsValue callWithArguments:params];
if (callback) {
callback(value, nil);
}
return value;
}
3、native使用jscontext 将js函数和block进行关联
js 逻辑代码会有很多的js 逻辑功能,但是这些逻辑功能最后都流转到 一个js方法上:
const invokeFlutterCommonChannel = (invokeData, callback) => {
console.log("invokeData" + invokeData)
jsInvokeFlutterChannel(invokeData, (resultStr) => {
console.log('resultStr' + resultStr);
if (callback) {
callback(resultStr);
}
});
};
jsInvokeFlutterChannel 方法对应的 native 中使用JSContext 环境对象,做了注册,以block 形式对应关联js功能:
两个js方法名字,这两个js方法都是在native的jscontext上下文中内置的, 这是js端 通过native-》dart方式回传到dart端的js方法:
NSString * const FairExecuteDartFunctionAsync = @"jsInvokeFlutterChannel";
NSString * const FairExecuteDartFunctionSync = @"jsInvokeFlutterChannelSync";
定义如下
- (JSContext *)context {
if (!_context) {
_context = [[JSContext alloc] init];
// 打印异常
_context.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue)
{
context.exception = exceptionValue;
FairLog(@"exceptionValue:%@", exceptionValue);
};
/**
* 以 block 形式关联 JavaScript function
*/
// 打印日志 - JS
_context[@"console"][@"log"] = ^(JSValue *value)
{
FairLog(@"%@", value);
};
// 打印日志 - Dart
_context[@"print"] = ^(JSValue *value)
{
FairLog(@"%@", value);
};
// 调试用,注意:js的alert会阻塞进程,导致卡住OC的主线程,所以拦截alert进行原生弹窗
_context[@"alert"] = ^(NSString *str) {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"提示" message:str preferredStyle:UIAlertControllerStyleAlert];
[alertVC addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]];
[[UIApplication sharedApplication].delegate.window.rootViewController presentViewController:alertVC animated:YES completion:nil];
});
};
FairWeakSelf(weakSelf);
// JS 异步调用 Dart
_context[FairExecuteDartFunctionAsync] = ^(id receiver, JSValue *callback) {
FairStrongObject(strongSelf, weakSelf)
NSString *data = [strongSelf convertStringWithData:receiver];
if ([strongSelf.delegate respondsToSelector:@selector(FairExecuteDartFunctionAsync:callback:)]) {
[strongSelf.delegate FairExecuteDartFunctionAsync:data callback:callback];
}
};
// JS 同步调用 Dart
_context[FairExecuteDartFunctionSync] = ^(id receiver, JSValue *callback) {
FairStrongObject(strongSelf, weakSelf)
NSString *data = [strongSelf convertStringWithData:receiver];
if ([strongSelf.delegate respondsToSelector:@selector(FairExecuteDartFunctionSync: callback:)]) {
[strongSelf.delegate FairExecuteDartFunctionSync:data callback:callback];
}
};
}
return _context;
}
_context[FairExecuteDartFunctionSync] 对应为一个block 调用 delegate 执行如下:
这个 delegate 是一个 FairJSExportDelegate 类型
#pragma mark - FairJSExportDelegate
/// JS 异步调用 Dart
- (void)FairExecuteDartFunctionAsync:(NSString *)data callback:(JSValue *)callback
{
[[FairDartBridge sharedInstance] sendMessageToDart:data callback:^(id result, NSError *error) {
[[FairJSBridge sharedInstance] invokeJSFunction:callback param:result];
}];
}
/// JS 同步调用 Dart
- (void)FairExecuteDartFunctionSync:(NSString *)data callback:(JSValue *)callback
{
[[FairDartBridge sharedInstance] sendMessageToDart:data callback:nil];
}
FairDartBridge 单例执行方法:
- (void)sendMessageToDart:(NSString *)message callback:(FairCallback)callback {
[self.flutterBasicMessageChannel sendMessage:message reply:^(id reply) {
if (callback && FAIR_IS_NOT_EMPTY_STRING(reply)) {
callback(reply, nil);
}
}];
}
回到调用 dart FlutterBasicMessageChannel:
NSString * const FairExecuteDartFunctionAsync = @"jsInvokeFlutterChannel";
NSString * const FairExecuteDartFunctionSync = @"jsInvokeFlutterChannelSync";
self.flutterBasicMessageChannel = [[FlutterBasicMessageChannel alloc]
initWithName:FairMessageChannelExecuteID
binaryMessenger:self.binaryMessenger
codec:[FlutterStringCodec sharedInstance]];
[self.flutterBasicMessageChannel sendMessage:message reply:^(id reply) {
if (callback && FAIR_IS_NOT_EMPTY_STRING(reply)) {
callback(reply, nil);
}
}];
因此 回到了 dart
4、native又重新调用 dart,回到dart
通过 ios native 的回调,执行的方法如下:
_commonChannel ??=
BasicMessageChannel<String?>(COMMON_MESSAGE_CHANNEL, StringCodec());
_commonChannel!.setMessageHandler((String? message) async {
print('来自native端的消息:$message');
//js 异步调用dart中的相关方法
var data = json.decode(message??'');
var funcName = data['funcName']?.toString();
if (funcName == 'invokePlugin') {
var p = await FairPluginDispatcher.dispatch(message);
return p;
}
_callback?.call(message);
return 'reply from dart';
});
这个具体的逻辑实现 就是在dart端了,我们看一下dispatch 方法,如何分发不同函数: FairPluginDispatcher 是一个类,封装了一个map 对象, 通过name寻找实现的功能映射表:
class FairPluginDispatcher {
static final pluginMap = <String, IFairPlugin>{};
static Future<dynamic> dispatch(dynamic msg) async {
dynamic obj;
if (msg is Map) {
obj = msg;
} else {
obj = jsonDecode(msg);
}
// var args = obj['args'];
var className = obj['className']?.toString();
if (className == null || className.isEmpty) {
return null;
}
if (className.contains('#')) {
className = className.split('#')[0];
}
if (pluginMap[className] != null) {
// var d = await ;
return pluginMap[className]?.invoke(msg);
}
}
static void registerPlugin(String key, IFairPlugin plugin) {
pluginMap[key] = plugin;
}
static void registerPlugins(Map<String, IFairPlugin>? plugins) {
if (plugins == null || plugins.isEmpty) {
return;
}
pluginMap.addAll(plugins);
}
}
下面找到注册了哪写逻辑函数到这个dispater对象中: 注册plugin:
FairPluginDispatcher.registerPlugins(plugins);
在flutter app启动时候,调用注册插件:
static void runApplication(Widget app, {Map<String, IFairPlugin>? plugins}) {
FairPluginDispatcher.registerPlugins(plugins);
}
FairApp.runApplication(
_getApp(),
plugins: {
'FairNet': FairNet(),
'WBPermission': WBPermission(),
'FairPhotoSelector': FairPhotoSelector(),
},
);
这个插件对象就是一个 IFairPlugin 类型。
dart 同步调用native
通过dart ffi,调用native动态库 c函数,dart侧实现如下:
final DynamicLibrary dl = Platform.isAndroid
? DynamicLibrary.open('libfairflutter.so')
: DynamicLibrary.open('FairDynamicFlutter.framework/FairDynamicFlutter');
Pointer<Utf8> Function(Pointer<Utf8>) invokeJSCommonFuncSync = dl
.lookup<NativeFunction<Pointer<Utf8> Function(Pointer<Utf8>)>>(
'invokeJSCommonFuncSync')
.asFunction();
dynamic sendCommonMessageSync(dynamic msg) =>
FairUtf8.fromUtf8(invokeJSCommonFuncSync.call(FairUtf8.toUtf8(msg)));
C 函数 声明部分:
#if defined(__cplusplus)
#define FAIR_FFI_EXTERN extern "C" __attribute__((visibility("default"))) __attribute__((used))
#else
#define FAIR_FFI_EXTERN extern __attribute__((visibility("default"))) __attribute__((used))
#endif
/// 同步方法调用
FAIR_FFI_EXTERN const char *invokeJSCommonFuncSync(char *args);
/// FFI协议
@protocol FAIRFFIProtocol <NSObject>
/// 同步方法调用
/// @param args 参数
- (const char *)executeScriptSyncImpl:(char *)args;
@end
@interface FairDynamicFlutter : NSObject
@property (nonatomic, weak) id<FAIRFFIProtocol> delegate;
+ (instancetype)sharedInstance;
@end
IOS native C函数实现部分:
const char *invokeJSCommonFuncSync(char *args) {
if ([FairDynamicFlutter sharedInstance].delegate &&
[[FairDynamicFlutter sharedInstance].delegate respondsToSelector:@selector(executeScriptSyncImpl:)]) {
return [[FairDynamicFlutter sharedInstance].delegate executeScriptSyncImpl:args];
}
return "";
}
[FairDynamicFlutter sharedInstance].delegate Delegate 方法实现FAIRFFIProtocol协议:
- (const char *)executeScriptSyncImpl:(char *)args
{
JSValue *obj;
if (self.delegate && [self.delegate respondsToSelector:@selector(executeJSFunctionSync:params:)]) {
NSString *str = [NSString stringWithUTF8String:args];
obj = [self.delegate executeJSFunctionSync:FairExecuteJSFunction params:@[str]];
}
NSString *result = [NSString stringWithFormat:@"%@", obj.toString];
FairLog(@"result:%@", result);
return result.UTF8String;
}
最后同样是调用到 ios native ,ios native 会去执行js 中的逻辑:
- (JSValue *)executeJSFunctionSync:(NSString *)functionName params:(NSArray *)params {
return [[FairJSBridge sharedInstance] invokeJSFunctionSync:functionName params:params];
}
- (JSValue *)invokeJSFunctionSync:(NSString *)functionName params:(NSArray *)params
{
return [self invokeJSFunctionOnJSThread:functionName params:params callback:nil];
}
- (JSValue *)invokeJSFunctionOnJSThread:(NSString *)functionName params:(NSArray *)params callback:(FairCallback)callback
{
JSValue *jsValue = self.context[functionName];
JSValue *value = [jsValue callWithArguments:params];
if (callback) {
callback(value, nil);
}
return value;
}
所以就是通过这么一段过程。 dart 的逻辑部分转化为js 文件
除了fair_core里面的js逻辑: fair_common_plugin.js far_core.js fair_jsbase.js 内置的基础配置json 文件为 fair_home.json 内容如下:
{
"coreJs": {
"fair_core": "packages/fair/assets/fair_core/fair_core.js",
"fair_jsbase": "packages/fair/assets/fair_core/fair_jsbase.js",
"fair_common_plugin": "packages/fair/assets/fair_core/fair_common_plugin.js"
}
}
其他的 plugin 的逻辑需要自己写,例如, fair_basic_plugin.js fair_net_plugin.js 写完放到一个json 配置文件下: fair_basic_config.json json 配置内容如下:
{
"plugin":{
"fair_net":"assets/plugin/fair_net_plugin.js",
"FairBasicPlugin":"assets/plugin/fair_basic_plugins.js"
}
}
widget 通过 flutter builder 自动生成的js 的文件内容如下:
FairNet js方法实现在插件中fair_net_plugin.js
//用户自定义拓展,需要在fair_basic_config.json中注册
//会在基础js加载之后加载
let callBack = {};
let callBackId = 0;
let FairNet = function () {
return {
requestRemoteData: function (resp) {
let respMap = {};
respMap = mapOrSetToObject(resp);
let id = 'FairNet$' + (++callBackId);
let requestParameter = {};
requestParameter['className'] = "FairNet#requestRemoteData";
callBack[id] = [respMap['complete'], respMap['error'], respMap['success']];
respMap['callId'] = id;
requestParameter['funcName'] = 'invokePlugin';
requestParameter['pageName'] = respMap['pageName'];
requestParameter['args'] = respMap;
let map = JSON.stringify(requestParameter);
console.log('FairNet请求参数:' + map);
invokeFlutterCommonChannel(map, (resultStr) => {
//省略了。。。
})
}
}
}
invokeFlutterCommonChannel 方法在 fair_core.js中定义如下:
const invokeFlutterCommonChannel = (invokeData, callback) => {
console.log("invokeData" + invokeData)
jsInvokeFlutterChannel(invokeData, (resultStr) => {
console.log('resultStr' + resultStr);
if (callback) {
callback(resultStr);
}
});
};
而这个js 方法 jsInvokeFlutterChannel 在ios Native中通过JSContext 定义了它的实现:
NSString * const FairExecuteDartFunctionAsync = @"jsInvokeFlutterChannel";
NSString * const FairExecuteDartFunctionSync = @"jsInvokeFlutterChannelSync";
// JS 异步调用 Dart
_context[FairExecuteDartFunctionAsync] = ^(id receiver, JSValue *callback) {
FairStrongObject(strongSelf, weakSelf)
NSString *data = [strongSelf convertStringWithData:receiver];
if ([strongSelf.delegate respondsToSelector:@selector(FairExecuteDartFunctionAsync:callback:)]) {
[strongSelf.delegate FairExecuteDartFunctionAsync:data callback:callback];
}
};
// JS 同步调用 Dart
_context[FairExecuteDartFunctionSync] = ^(id receiver, JSValue *callback) {
FairStrongObject(strongSelf, weakSelf)
NSString *data = [strongSelf convertStringWithData:receiver];
if ([strongSelf.delegate respondsToSelector:@selector(FairExecuteDartFunctionSync: callback:)]) {
[strongSelf.delegate FairExecuteDartFunctionSync:data callback:callback];
}
};
/// JS 同步调用 Dart
- (void)FairExecuteDartFunctionSync:(NSString *)data callback:(JSValue *)callback
{
[[FairDartBridge sharedInstance] sendMessageToDart:data callback:nil];
}
- (void)sendMessageToDart:(NSString *)message callback:(FairCallback)callback {
[self.flutterBasicMessageChannel sendMessage:message reply:^(id reply) { if (callback && FAIR_IS_NOT_EMPTY_STRING(reply)) { callback(reply, nil); } }];
}
通过第一步源代码分析知道了,这就会回调到 dart 侧。
整体调用流程
上面是dart ios js的调用流转过程。下面从 使用 fair 逻辑动态化的使用步骤,走一遍过程。