Fair release 4.0:实现更便捷的动态插件功能

219 阅读4分钟

FairRelease4.0插件新设计

FairCommonPlugin通用插件 所有需要到的动态化逻辑,支持使用FairCommonPlugin这个插件的方式来进行扩展, 完成动态逻辑的实现。当然原有的插件的实现方式仍然可用。

如何开发插件

1、编写FairCommonPluginMixin

fair_toast_plugin.dart 将此文件放到 lib/src/plugin/目录下面:

    mixin FairHttpPlugin implements FairCommonPluginMixin {
      Future<dynamic> http(dynamic map) => request(map, _run);

      Future<Map?> _run(Map requestMap) async {
        // implements http here.
        final method = requestMap['method'];
        final url = requestMap['url'];
        Response<String>? result;
        switch (method) {
          case 'GET':
            result = await _get(url);
            break;
          case 'POST':
            result = await _post(url);
            break;
          default:
        }
        if (result != null) {
          return {
            'data': result.data == null ? '' : jsonDecode(result.data!),
            'statusCode': result.statusCode,
          };
        }
        return null;
      }

      static Future<Response<String>> _post(String path,
          {Map<String, String>? queryParameters}) async {
        var resp =
            await _getDio().post<String>(path, queryParameters: queryParameters);
        return Future.value(resp);
      }

      static Future<Response<String>> _get(String path,
          {Map<String, String>? queryParameters}) async {
        var resp =
            await _getDio().get<String>(path, queryParameters: queryParameters);
        return Future.value(resp);
      }

      static Dio? _dio;

      static Dio _getDio() {
        _dio ??= Dio();
        return _dio!;
      }
    }

注意这的位置,一定要是lib/src目录下,且实现implements FairCommonPluginMixin

2、执行脚本 生成js和将方法注册

输入命令:

    dart run bin/fair_common_plugin.dart

得到产物:

产物1:/assets/plugin/fair_common_plugin.js

产物2:/lib/src/plugin/fair_common_plugin.dart

这样就完成了:

在js端实现了FairCommonPlugin().http() 方法

在dart端class FairCommonPlugin extends IFairPlugin插件中getRegisterMethods对http方法进行了注册。

3、demo中调用

导入插件:

    import 'package:example/src/plugin/fair_common_plugin.dart';
    import 'package:fair/fair.dart';

在页面中调用逻辑动态化插件:

    ///build方法中调用:
    build(){ 
     ///省略代码
        Container(
                    alignment: Alignment.center,
                    child: ElevatedButton(
                        onPressed: commonHttp,
                        child: Text('网络请求-基于FairCommonPlugin')),
                  ),
     ///省略代码
    }

    ///调用
      commonHttp() {
        FairCommonPlugin().http({
          'method': 'GET',
          'url':
              'https://wos2.58cdn.com.cn/DeFazYxWvDti/frsupload/3b8ae7a4e0884b4d75b8094f6c83cd8c_list_page_data.json',
          'callback': (dynamic result) {
            if (result != null) {
              var statusCode = result['statusCode'];
              if (statusCode == 200) {
                var list = result['data']['data'];
                list.forEach((item) {
                  var icon = item['icon'];
                  print('icon = $icon');
                });
              }
            }
          }
        });
      }

注意:一定需要使用package://包名导入,不要使用 相对路径方式导入'../src/plugin/fair_common_plugin.dart';

这种是正常的: image.png

使用相对路径会使第二个参数是[1]就,导致js报错 image.png

fair js调用dart流程解析:

调用顺序:

0、业务调用FairCommonPlugin().xxx方法

    FairCommonPlugin().http(convertObjectLiteralToSetOrMap({
                                ['method']: 'GET',
                                ['url']: 'https://wos2.58cdn.com.cn/DeFazYxWvDti/frsupload/3b8ae7a4e0884b4d75b8094f6c83cd8c_list_page_data.json',
                                ['callback']: function dummy(result) {
                                    if (result != null) {
                                        let statusCode = result.__op_idx__('statusCode');
                                        if (statusCode == 200) {
                                            let list = result.__op_idx__('data').__op_idx__('data');
                                            list.forEach(function dummy(item) {
                                                let icon = item.__op_idx__('icon');
                                                print(`icon = ${icon}`);
                                            });
                                        }
                                    }
                                },
                            }));

1、FairCommonPlugin().xxx实现

/example/assets/plugin/fair_common_plugin.js

// 由 bin/fair_common_plugin.dart 生成
let FairCommonPlugin = function () {
    return {
        futureComplete: function (resp) {
             fairCommonPluginRequest(resp, 'futureComplete');
        },
        http: function (resp) {
             fairCommonPluginRequest(resp, 'http');
        },
        pushNamed: function (resp) {
             fairCommonPluginRequest(resp, 'pushNamed');
        },
        pushFairPath: function (resp) {
             fairCommonPluginRequest(resp, 'pushFairPath');
        },
        pop: function (resp) {
             fairCommonPluginRequest(resp, 'pop');
        },
        toast: function (resp) {
             fairCommonPluginRequest(resp, 'toast');
        }                     
    }
}

2、 fairCommonPluginRequest

Fair/fair/assets/fair_core/fair_common_plugin.js

    //用户自定义拓展,需要在fair_basic_config.json中注册
    //会在基础js加载之后加载
    let _callBack = {};
    let _callBackId = 0;
    let fairCommonPluginRequest = function (resp, methodName) {
        let respMap = {};
        respMap = mapOrSetToObject(resp);
        let id = 'FairCommonPlugin$' + (++_callBackId);
        let requestParameter = {};
        requestParameter['pageName'] = '#FairKey#';
        // 类名 + 方法名
        requestParameter['className'] = "FairCommonPlugin#" + methodName;
        _callBack[id] = respMap['callback'];
        respMap['callId'] = id;
        // 代码里面有判断 funcName 必填
        requestParameter['funcName'] = 'invokePlugin';
        requestParameter['request'] = respMap;
        let map = JSON.stringify(requestParameter);
        console.log('FairCommonPlugin请求参数:' + map);
        invokeFlutterCommonChannel(map, (resultStr) => {
            console.log('FairCommonPlugin请求结果:' + resultStr);
            let responseMap = JSON.parse(resultStr);
            console.log('FairCommonPlugin请求结果1:' + responseMap);
            let id = responseMap['callId']
            let data = responseMap['response'];
            console.log('FairCommonPlugin请求结果2:' + id);
            // 这两个函数用户拓展的
            if (_callBack[id] === null) {
                return;
            }
            let complete = _callBack[id];
            console.log('FairCommonPlugin请求结果3:' + data);
            // 返回的是 map
            if (data === null) {
                complete(null);
            }
            else {
                complete(convertObjectLiteralToSetOrMap(data));
            }
            _callBack[id] = null
        })
    }

3、invokeFlutterCommonChannel

const invokeFlutterCommonChannel = (invokeData, callback) => {
    console.log("invokeData" + invokeData)
    jsInvokeFlutterChannel(invokeData, (resultStr) => {
        console.log('resultStr' + resultStr);
        if (callback) {
            callback(resultStr);
        }
    });
};

4、jsInvokeFlutterChannel

/Fair/fair/ios/Classes/FairDynamicJSPlugin/FairJSBridge.m

            // 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];
                }
            };

5、BasicMessageChannel 通讯调用dart

com.wuba.fair/common_message_channel

/Fair/fair/lib/src/runtime/fair_message_channel.dart

        _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';
        });

6、FairPluginDispatcher 从已注册插件表pluginMap中查找实现

/Fair/fair/lib/src/runtime/plugin/plugin_dispatcher.dart

      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);
        }
      }

7、Function.apply 调用对应的方法

/Fair/fair/lib/src/runtime/plugin/fair_plugin.dart


abstract class IFairPlugin with FairCommonPluginMixin {
  Future<dynamic> invoke(dynamic par) async {
    var resp =
        await Function.apply(getRegisterMethods()[getMethodName(par)]!, [par]);
    return Future.value(resp);
  }

  String getMethodName(dynamic par) {
    dynamic a;
    if (par is Map) {
      a = par;
    } else {
      a = jsonDecode(par);
    }

    var name = a['className']?.toString() ?? '';

    if (name.contains('#')) {
      var list = name.split('#');
      if (list.length >= 2) {
        return list[1];
      }
    }

    return '';
  }

8、 从目标插件中的方法列表中 getRegisterMethods查找对应的实现function

    class FairCommonPlugin extends IFairPlugin
        with CompleterPlugin, FairHttpPlugin, FairNavigatorPlugin, FairToastPlugin {
      factory FairCommonPlugin() => _fairCommonPlugin;
      FairCommonPlugin._();
      static final FairCommonPlugin _fairCommonPlugin = FairCommonPlugin._();
      @override
      Map<String, Function> getRegisterMethods() {
        return <String, Function>{
          'futureComplete': futureComplete,
          'http': http,
          'pushNamed': pushNamed,
          'pushFairPath': pushFairPath,
          'pop': pop,
          'toast': toast,
        };
      }
    }

9、FairCommonPluginMixin 真正的dart逻辑

/Fair/example/lib/src/plugin/fair_http_plugin.dart

mixin FairHttpPlugin implements FairCommonPluginMixin {
  Future<dynamic> http(dynamic map) => request(map, _run);

  Future<Map?> _run(Map requestMap) async {
    // implements http here.
    final method = requestMap['method'];
    final url = requestMap['url'];
    Response<String>? result;
    switch (method) {
      case 'GET':
        result = await _get(url);
        break;
      case 'POST':
        result = await _post(url);
        break;
      default:
    }
    if (result != null) {
      return {
        'data': result.data == null ? '' : jsonDecode(result.data!),
        'statusCode': result.statusCode,
      };
    }
    return null;
  }

  static Future<Response<String>> _post(String path,
      {Map<String, String>? queryParameters}) async {
    var resp =
        await _getDio().post<String>(path, queryParameters: queryParameters);
    return Future.value(resp);
  }

  static Future<Response<String>> _get(String path,
      {Map<String, String>? queryParameters}) async {
    var resp =
        await _getDio().get<String>(path, queryParameters: queryParameters);
    return Future.value(resp);
  }

  static Dio? _dio;

  static Dio _getDio() {
    _dio ??= Dio();
    return _dio!;
  }
}

10、通用方法request

/Fair/fair/lib/src/runtime/plugin/fair_common_plugin.dart

源码:

    mixin FairCommonPluginMixin {
      /// common request method
      Future<dynamic> request(
        dynamic map,
        // do your business logic in this call back
        Future<Map?> Function(Map reqData) run,
      ) async {
        if (map == null) {
          return;
        }
        var req;
        bool isDart;
        if (map is Map) {
          isDart = true;
          req = map;
        } else {
          isDart = false;
          req = jsonDecode(map);
        }
        final pageName = req['pageName'];
        var request = req['request'];
        if (isDart) {
          request = req;
        }

        final callId = request['callId'];

        final completeCallback = request['callback'];

        final response = await run(request);

        // 需要判断发起方的请求是dart端还是js端
        if (isDart) {
          completeCallback?.call(response);
          return Future.value();
        } else {
          final resp = {
            'callId': callId,
            'pageName': pageName,
            'response': response,
          };
          return Future.value(jsonEncode(resp));
        }
      }
    }

处理通用的逻辑:

js参数解析:

js业务参数 :request。 通用参数:pageName、request、 request参数:js调用 callId,dart调用callback、业务参数FairCommonPlugin().xx({}///这里传递的参数)

dart回调参数: 'callId': callId, 'pageName': pageName, 'response': response,

处理特有的逻辑:

implements FairCommonPluginMixin中实现:

      Future<Map?> _run(Map requestMap) async {
    //只管返回业务的数据。 js dart通讯参数不需要管。
    }

结束语

本文详细介绍了FairRelease 4.0插件的全新设计,重点在于利用FairCommonPlugin扩展插件。通过自动生成JavaScript逻辑代码和对应的Dart侧方法注册脚本,避免了手动编写的繁琐工作,使我们能够更专注于业务逻辑的开发实现,显著提高了接入效率。