58fair源码阅读js如何调用dart端执行setState界面刷新

native端实现(iOS)

FlutterBasicMessageChannel channel通道

ini 复制代码
NSString * const FairMessageChannelExecuteID = @"com.wuba.fair/common_message_channel";  //


self.flutterBasicMessageChannel = [[FlutterBasicMessageChannel alloc] initWithName:FairMessageChannelExecuteID //使用FlutterBasicMessageChannel类型的channel通道
                                                                   binaryMessenger:self.binaryMessenger
                                                                             codec:[FlutterStringCodec sharedInstance]];

js调用dart,结果callback回调js

objectivec 复制代码
- (void)sendMessageToDart:(NSString *)message callback:(FairCallback)callback {
    [self.flutterBasicMessageChannel sendMessage:message reply:^(id reply) { //调用dart
        if (callback && FAIR_IS_NOT_EMPTY_STRING(reply)) {//dart返回结果,调用callback
            callback(reply, nil);
        }
    }];
}

- (void)FairExecuteDartFunctionAsync:(NSString *)data callback:(JSValue *)callback
{
    [[FairDartBridge sharedInstance] sendMessageToDart:data callback:^(id result, NSError *error) {//调用dart
        [[FairJSBridge sharedInstance] invokeJSFunction:callback param:result]; //dart返回结果result,回调js callback
    }];
}

通用js方法名定义

通用名字的js方法定义,js方法名通过channel调用dart,dart执行结果callback 回传js

less 复制代码
NSString * const FairExecuteDartFunctionAsync = @"jsInvokeFlutterChannel";

NSString * const FairExecuteDartFunctionSync = @"jsInvokeFlutterChannelSync";


// JS 异步调用 Dart
_context[FairExecuteDartFunctionAsync] = ^(id receiver, JSValue *callback) { //注册js 方法,通用的js方法名
    FairStrongObject(strongSelf, weakSelf)
    
    NSString *data = [strongSelf convertStringWithData:receiver];
    if ([strongSelf.delegate respondsToSelector:@selector(FairExecuteDartFunctionAsync:callback:)]) {
        [strongSelf.delegate FairExecuteDartFunctionAsync:data callback:callback];//js调用dart,结果回调js
    }
};

// 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端定义

fair_core.js

javascript 复制代码
function setState(pageName, obj) {
    console.log('JS:setState()_before' + pageName + '-' + obj);
    let p = {};
    p['funcName'] = 'setState';
    p['pageName'] = pageName;
    // console.log('JS:setState(states)'+JSON.stringify(Object.getOwnPropertySymbols(obj)));
    obj();
    p['args'] = null;
    let map = JSON.stringify(p);
    console.log('JS:setState()' + map);
    invokeFlutterCommonChannel(map);
}


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

业务逻辑js文件

javascript 复制代码
GLOBAL['#FairKey#'] = (function(__initProps__) {
    const __global__ = this;
    return runCallback(function(__mod__) {
    
        ///.....省略
        setState('#FairKey#', function dummy() {});
    
        ///.....省略
    
    }
})(convertObjectLiteralToSetOrMap(JSON.parse('#FairProps#'))); 

逻辑JS文件FairWidget加载

FairWidget渲染界面定义

scss 复制代码
    @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _fairApp ??= FairApp.of(context);
    //加载js的文件地址
    _resolveFairRes(_fairApp!, FairJSFairJSDecoderHelper.transformPath(widget.path));
  }

加载所有JS逻辑文件

dart 复制代码
Future<dynamic> _resolveFairRes(FairApp _mFairApp, String? jsPath) async {


  final results = await Future.wait([
        _mFairApp.runtime.addScript(state2key, resolveJS, widget.data),
        _mFairApp.register(this)
      ]);

}

逻辑JS文件内容加载到内存,进行#FairKey##fairProps#模式字符串匹配替换,替换成实际的运行时的变量值

python 复制代码
  @override
  Future<dynamic> addScript(String pageName, String scriptSource, dynamic props) async {
    // var scriptSource = await rootBundle.loadString(script);
    var fairProps;
    if (props != null && props['fairProps'] != null) {
      fairProps = props['fairProps'];
    } else {
      fairProps = '{}';
    }
    if (fairProps is String) {
      fairProps = fairProps.replaceAll('\\', '\\\\');
    }
    scriptSource = scriptSource.replaceFirst(RegExp(r'#FairProps#'), fairProps);
    scriptSource = scriptSource.replaceAll(RegExp(r'#FairKey#'), pageName);
    var map = <dynamic, dynamic>{};
    map[FairMessage.PATH] = scriptSource;
    map[FairMessage.PAGE_NAME] = pageName;
    return _channel!.loadJS(jsonEncode(map), null);
  }

dart端实现

BasicMessageChannel通道注册handler回调,监听native事件调用

ini 复制代码
final String COMMON_MESSAGE_CHANNEL = 'com.wuba.fair/common_message_channel';

_commonChannel ??=
    BasicMessageChannel<String?>(COMMON_MESSAGE_CHANNEL, StringCodec());


_commonChannel!.setMessageHandler((String? message) async { //监听native消息
  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); //执行native消息,一般就是js调用dart,由js发出的 
  return 'reply from dart';
});

callback方法绑定,即通过channel监听到事件后的分发处理

ini 复制代码
void setMessageHandler(StringMsgCallback callback) { 
  _callback = callback; //_callback
}



Runtime._internal() {
    init(true);

    _channel ??= FairMessageChannel();
 
    //接收setState()的信息
    _channel!.setMessageHandler((message) { //_callback  ①这个callback其实不会调用的
      var data = json.decode(message ?? '');
      var className = data['pageName'];
      var call = _callBacks[className];
      call?.call(message); //
      return null;
    });
}

另外一种方法callback的绑定

void bindCallback(String key, RuntimeCallback callback) { //_callback _callBacks[key] = callback; }

通过FairHandler对象对channel事件消息进行处理

kotlin 复制代码
FairHandler(this._runtime) {
    //接收native发送过来的消息,实际上是js发送的消息,通过native端透传过来
    _runtime.getChannel().setMessageHandler((String? message) {   //callback ②会调用这个callback
      var data = json.decode(message ?? '');
      var funcName = data['funcName']?.toString();
      var pageName = data['pageName'];
      var args = data['args']??{};

      if (funcName == null || funcName.isEmpty) {
        return '';
      }

      //当用户调用setState的时候相当于刷新了数据,通知刷新页更新
      if (funcName == 'setState') {
        _dispatchMessage(pageName, jsonEncode(args));
        return '';
      }

      // //js 异步调用dart中的相关方法
      // if (funcName == 'invokePlugin') {
      //   FairPluginDispatcher.dispatch(jsonEncode(args))
      //       .then((value) => _runtime.getChannel().sendCommonMessage(value));
      //   return '';
      // }
      return '';
    });
}

AppState

每一个fairWidget的FairState状态都在全局AppState中进行管理

scala 复制代码
mixin AppState {
  final modules = FairModuleRegistry();
  final bindData = <String, BindingData>{};
  final _proxy = ProxyMirror();
  final runtime = Runtime(); //这里一个runtime
  final _mFairHandler = FairHandler(Runtime()); //这里有一个runtime 这是两个不同的runtime对象 

}


class FairApp extends InheritedWidget with AppState 

 
Future<dynamic> register(FairState state) async {//每一个FairWidget都会调用
    log('register state: ${state.state2key}');
    _mFairHandler.register(state); //_mFairHandler 这里用。内部一个runtime对象逻辑家
    var delegate = state.delegate;
    delegate.setRunTime(runtime); //这里使用的runtime对象
    // await delegate.bindAll({});
    bindData.putIfAbsent(
      state.state2key,
      () => BindingData(
        modules,
        functions: delegate.bindFunction(),
        values: delegate.bindValue(),
      ),
    );
    return Future.value(null);
}

runtime单例对象

通过定义单例对象,channel通道交互都在同一处执行

scss 复制代码
final runtime = Runtime(); //这里一个runtime
delegate.setRunTime(runtime); //这里使用的runtime对象
final _mFairHandler = FairHandler(Runtime()); //这里也创建一个runtime

FairHandler(this._runtime) {
    //接收native发送过来的消息,实际上是js发送的消息,通过native端透传过来
    _runtime.getChannel().setMessageHandler((String? message) {
    }
}

class Runtime implements IRuntime {
 
  static final Runtime _runtime = Runtime._internal();
 
  factory Runtime() {//构造方法 创建出来的是一个静态全局变量。所以这个runtime对象是一个单例对象
    return _runtime;
  }

  Runtime._internal() {
    init(true);

    _channel ??= FairMessageChannel();
 
  }
}

FairWidget state setState调用

js如何调用dart setState刷新界面?

每一个FairWidget对应的State都有一个stateKey做为键值在全局表中存储

因为runtime dart通道是一个全局对象,这里面所有的js调用dart端的方法都将在上述所描述的setMessageHandler消息处理中分发,那一个页面中同时多个Fairwidget如何找到自己对应的Flutter State对象呢,这就是通过map存储:

我们先了解到:每一个FairWidget都有一个自己唯一标识 state2key,这个state2key 是由pageName和静态ID自增拼接,避免同一个FairWidget多次使用重复问题。

scala 复制代码
 
class FairState extends State<FairWidget> with Loader, AutomaticKeepAliveClientMixin<FairWidget> implements FairMessageCallback<String> {

late String state2key;

  @override
void initState() {
  super.initState();
  state2key = GlobalState.id(widget.name);

}

static String id(String? prefix) {
  return '$prefix#${GlobalState._counter++}';
}


String getMessageKey() => state2key;


}

_mFairHandler.register(state);

定义一个map对象pageHistories 存储每一个页面的

javascript 复制代码
class FairHandler { 

  final pageHistories = <String, FairMessageCallback<String>>{};
  final Runtime _runtime;


  String _dispatchMessage(String pageName, String message) {
    pageHistories[pageName]?.call(message);
    return 'Reply from Dart';
  }

  void register(FairMessageCallback<String> state) {
    log('register state: ${state.getMessageKey}');
    pageHistories[state.getMessageKey()] = state;
  }

  void unregister(FairMessageCallback<String> state) {
    var key = state.getMessageKey();
    log('unregister state: $key');
    pageHistories.remove(key);
  }


}

 

FairHandler是一个app级别的对象:

scss 复制代码
mixin AppState {

  final _mFairHandler = FairHandler(Runtime());


  Future<dynamic> register(FairState state) async {

    _mFairHandler.register(state);
  
  }


}

FairState 实现 FairMessageCallback协议

FairState持有 FairDelegate delegate对象,这个delegate 是Fair运行时所有相关运行时操作的一个封装,其中一个就是调用setState方法。

通过上面的FairHandler的pageHistories这个map表,通过stateKey找到对应state,由state的delegate对象,调用state_state?.setState(fn);方法。

FairMessageCallback 定义:

csharp 复制代码
abstract class FairMessageCallback<T> {
  void call(T t);

  String getMessageKey();
}

FairState 实现 FairMessageCallback协议:

scala 复制代码
class FairState extends State<FairWidget> with Loader, AutomaticKeepAliveClientMixin<FairWidget> implements FairMessageCallback<String> {


  @override
  void call(String t) {
    var params={};
    try {
      params= jsonDecode(t);
    } catch (e) {
      print(e);
    }
    delegate.notifyValue(params); //
  }

  @override
  String getMessageKey() => state2key;


  void initState() {
    super.initState();
    state2key = GlobalState.id(widget.name);
    delegate = widget.delegate ?? GlobalState.of(widget.name).call(context, widget.data); //获取delegate对象从FairWidget传的或是是FairApp对象中创建的从中获取全局delegate
    delegate._bindState(this);//保存当前 Widget state对象d
    delegate.initState();  //调用delegate的initState方法 用户可以在FairDelegatec重写一些自定义操作
  }

}

FairDelegate 定义:

typescript 复制代码
class FairDelegate extends RuntimeFairDelegate {
  FairState? _state;
 late String _key;

  void _bindState(FairState? state) {
    assert(state != null, 'FairState should not be null');
    _state = state!;
    _key = state.state2key;
  }

 
  @override
  void setState(VoidCallback fn) {
    if (_state == null || !_state!.mounted) return;
    // ignore: invalid_use_of_protected_member
    _state?.setState(fn); //这里调用state对象的setState的刷新界面
    _state?._reload();
  }

  //获取js端的数据,刷新指定数据
  void notifyValue(Map values) {
    // values.forEach((key, value) {
    //   _valueMap[key]?.value = value;
    // });
    setState(() {});
  }

}

总结

综合上面代码分析,我们就知道了FairWidget渲染完成后,页面中的逻辑部分转换成了js被加载到内存中,当执行js逻辑功能时,通过native端注册的js通用方法名字的_context[FairExecuteDartFunctionAsync]方法,通过native端FlutterBasicMessageChannel类型的flutterBasicMessageChannel和dart端类型为BasicMessageChannel_commonChannel调用dart,dart通过消息监听进行分发,每一个FairWidget对应一个唯一的statekey,每一个state存储到一个FairApp的全局map对象中,当执行setState方法时,js端传入pageName,从全局map中取出state调用其setState方法。这样的一个过程就完成了调用dart端执行setState界面刷新的全部流程。

相关推荐
HarderCoder7 小时前
iOS 知识积累第一弹:从 struct 到 APP 生命周期的全景复盘
ios
孤鸿玉11 小时前
Fluter InteractiveViewer 与ScrollView滑动冲突问题解决
flutter
叽哥18 小时前
Flutter Riverpod上手指南
android·flutter·ios
BG1 天前
Flutter 简仿Excel表格组件介绍
flutter
zhangmeng2 天前
FlutterBoost在iOS26真机运行崩溃问题
flutter·app·swift
恋猫de小郭2 天前
对于普通程序员来说 AI 是什么?AI 究竟用的是什么?
前端·flutter·ai编程
卡尔特斯2 天前
Flutter A GlobalKey was used multipletimes inside one widget'schild list.The ...
flutter
w_y_fan2 天前
Flutter 滚动组件总结
前端·flutter
醉过才知酒浓2 天前
Flutter Getx 的页面传参
flutter
用户092 天前
SwiftUI Charts 函数绘图完全指南
ios·swiftui·swift