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界面刷新的全部流程。

相关推荐
kirk_wang4 分钟前
Flutter适配HarmonyOS实践
flutter·华为·harmonyos
iOS民工1 小时前
iOS SSZipArchive 解压后 中文文件名乱码问题
ios
皮蛋很白5 小时前
IOS safari 播放 mp4 遇到的坎儿
前端·ios·safari·video.js
你听得到119 小时前
《Flutter性能优化全攻略:从首屏渲染到性能监测,附案例代码详解》
android·flutter
aakzhangliangming11 小时前
乐乐音乐Flutter版
flutter·音乐播放器·桌面开发·动感歌词·乐乐音乐·动感歌词转换器
江上清风山间明月19 小时前
Flutter DragTarget拖拽控件详解
android·flutter·ios·拖拽·dragtarget
Kaelinda1 天前
iOS开发代码块-OC版
ios·xcode·oc
江上清风山间明月1 天前
flutter bottomSheet 控件详解
android·flutter·底部导航·bottomsheet
ii_best2 天前
ios按键精灵自动化的脚本教程:自动点赞功能的实现
运维·ios·自动化
yuanlaile2 天前
纯Dart Flutter库适配HarmonyOS
flutter·华为·harmonyos·flutter开发鸿蒙·harmonyos教程