58fair逻辑动态化实现原理iOS端

131 阅读3分钟

dart ⇋ iOS Native ⇋ js

  • dart 异步调用 native

  • dart 同步调用 native

dart 异步调用 native

未命名文件 (5).png

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

未命名文件 (4).png

通过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 的文件内容如下:

image.png

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 逻辑动态化的使用步骤,走一遍过程。

FairDelegate 使用

FairPlugin 使用