通过58 fair源代码学习发现一个 ios通道的 冗余的代码实现

166 阅读3分钟

通过58 fair源代码学习发现一个 ios通道的 冗余的代码实现
flutterCallbackMessageChannel

1、我们先从源码文件/Users/x/Desktop/fair/fair/lib/src/app.dart中看到有几行代码实现如下:

  static void runApplication(Widget app, {
  Map<String, IFairPlugin>? plugins, 
  Map<String, String>? jsPlugins, 
  String? package, 
  List<String>? baseJsSources,
  }) {
    // WidgetsFlutterBinding.ensureInitialized();
    FairPluginDispatcher.registerPlugins(plugins);

    if(!kIsWeb && Platform.isAndroid){
      var runtime = Runtime();
      var basicChannel = runtime.getBasicChannel();
      basicChannel.invokeMethod('jsLoadListener').then((value){
        runtime.loadCoreJs(package: package, jsPlugins: jsPlugins, baseJsSources: baseJsSources).then((value) => runApp(app));
      });

    }else{
      Runtime().loadCoreJs(package: package, jsPlugins: jsPlugins, baseJsSources: baseJsSources).then((value) => runApp(app));
    }
  }
}

就是上面这部分代码中,看到:

var basicChannel = runtime.getBasicChannel();
basicChannel.invokeMethod 

这行代码需要区别调用 ios 和安卓,打开if-else会发现 ios上 basicChannel.invokeMethod 是不会有回调的

2、看一下这个 runtime 对象类型的实现

class Runtime implements IRuntime {
  final loadBaseJsConstant = [false];
  static final Runtime _runtime = Runtime._internal();
  final _callBacks = <String, RuntimeCallback>{};

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

  factory Runtime() {
    return _runtime;
  }

  MethodChannel getBasicChannel(){
    return _channel!.basicMethodChannel!;
  }

  FairMessageChannel? _channel;

  Runtime._internal() {
    init(true);

    _channel ??= FairMessageChannel();
    //接收setState()的信息
    _channel!.setMessageHandler((message) {
      var data = json.decode(message ?? '');
      var className = data['pageName'];
      var call = _callBacks[className];
      call?.call(message);
      return null;
    });
 }
 
  FairMessageChannel getChannel() {
    return _channel!;
  }
 
}

可以看到上面的getBasicChannel 这个就是对应为 FairMessageChannel 类中定义的 basicMethodChannel

3、查看 FairMessageChannel 类

同样我去掉了不相关的代码,摘取了需要关心的部分:

 
final String BASIC_MESSAGE_CHANNEL = 'com.wuba.fair/basic_message_channel';

class FairMessageChannel {
 

  BasicMessageChannel<String?>? _commonChannel;
  MethodChannel? _methodChannel;
  MethodChannel? basicMethodChannel;
 
  void _initMessageChannel() {
 
    basicMethodChannel ??= MethodChannel(BASIC_MESSAGE_CHANNEL);
  } 
}

可以看到了, dart 这边我们是实现了一个 MethodChannel(BASIC_MESSAGE_CHANNEL); 的通道。

4、回到 ios native部分

在ios 的实现部分, 我们去找 名字为com.wuba.fair/basic_message_channel的实现

NSString * **const** FairBasicMessageChannelExecuteID = @"com.wuba.fair/basic_message_channel";
 

在文件/Users/x/Desktop/fair/fair/ios/Classes/FairDynamicJSPlugin/FairDartBridge.m中有:


#import "FairDartBridge.h"
#import "FairDefine.h"
#import <Flutter/Flutter.h>
#import "FairDartModel.h"

@interface FairDartBridge ()

/// js注入通道
@property (nonatomic, strong) FlutterMethodChannel *flutterMethodChannel;
/// Fair的flutter通道
@property (nonatomic, strong) FlutterBasicMessageChannel *flutterBasicMessageChannel;
/// fair回调通道
@property (nonatomic, strong) FlutterBasicMessageChannel *flutterCallbackMessageChannel;
/// binaryMessenger
@property (nonatomic, weak) NSObject<FlutterBinaryMessenger> *binaryMessenger;

@end

@implementation FairDartBridge

FairSingletonM(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);
        }
    }];
}

- (void)setDartChannelWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
    // 设置同步执行代理
    [FairDynamicFlutter sharedInstance].delegate = self;
 
    self.flutterCallbackMessageChannel = [[FlutterBasicMessageChannel alloc] initWithName:FairBasicMessageChannelExecuteID
                                                                          binaryMessenger:self.binaryMessenger
                                                                                    codec:[FlutterStringCodec sharedInstance]];
    
   
    
}

@end

是有一个flutterCallbackMessageChannel 通道对象的定义,但是这个对象却被定义为 FlutterBasicMessageChannel 类型的。

dart 是 MethodChannel类型对象,ios这边是 FlutterBasicMessageChannel 类型,两边定义不一致。

但是即使定义的通道类型不一致。却依然能够互通。

dart侧调用:

var runtime = Runtime();
var basicChannel = runtime.getBasicChannel();
basicChannel.invokeMethod('jsLoadListener').then((value){
  Runtime().loadCoreJs(package: package, jsPlugins: jsPlugins, baseJsSources: baseJsSources).then((value) => runApp(app));
});

参数为字符串 jsLoadListener

iOS端实现:

[self.flutterCallbackMessageChannel setMessageHandler:^(id  _Nullable message, FlutterReply  _Nonnull callback) {
        FairStrongObject(strongSelf, weakSelf);
        FairLog(@"%@", message);
        
        if (strongSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(executeJSFunctionAsync:params:callback:)]) {
            NSArray *params = message && [message isKindOfClass:[NSString class]] ? @[message] : @[];
            
            //调用js方法
            [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);
                    }
                }
            }];
        }
    }];

打印如下:

image.png iOS端参数前面替那家\a\u00000000这种字符 :

image.png

代码如下:

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

这里 js方法名为 invokeJSFunc,参数为 jsLoadListener ,

fair_core.js

在app 启动时候 还没有被加载。

所以fair_core.js中的方法

function invokeJSFunc(parameter) {
    if (parameter === null) {
        return null;
    }
    let map = JSON.parse(parameter);
    if ('method' === map['type']) {
        return _invokeMethod(map);
    } else if ('variable' === map['type']) {
        return _invokeVariable(map);
    }
    return null;
}

此刻是没有加载进来的。

回到上面的channel定义,

   if (![str isEqualToString:@"undefined"] && FAIR_IS_NOT_EMPTY_STRING(str)) {
                        callback(str);
                    }

如果是 undefined ,则不会调用callback,导致不会回传

basicChannel.invokeMethod('jsLoadListener').then((value){不会执行then方法的,会卡在这里。

所以 flutterCallbackMessageChannel 这个对象是用来做什么用的呢?还有basicChannel.invokeMethod('jsLoadListener') 这个在安卓上 主要是做了什么呢,需要加上这么一个前置条件。

5、channel通道定义回顾:

回顾一下各端实现:

ios: image.png

ios-messagechannel.png

dart:

image.png

image.png

android:

image.png

image.png

jsLoadListener.png

可以看到:这个com.wuba.fair/basic_message_channel通道的定义两端的作用感觉是有问题的。

对于jsLoadListener安卓端的调用是直接回调,没有任何逻辑。

而iOS只能是通过if-else不去调用,因为定义的通道类型不匹配,并且实现也和安卓不相同,ios是去执行js方法,然而js方法jsLoadListener又没有加载到JavascriptCore环境中因而也会返回invliad方法不存在,所以dart端用了if-else判断,ios不执行这个channel和jsLoadListener方法。

6、总结:

  • flutter端
basicMethodChannel ??= MethodChannel(BASIC_MESSAGE_CHANNEL);

ios端

self.flutterCallbackMessageChannel = 
[[FlutterBasicMessageChannel alloc] 
   initWithName:FairBasicMessageChannelExecuteID
binaryMessenger:self.binaryMessenger
          codec:[FlutterStringCodec sharedInstance]];

虽然也能互通,但是js方法调不通,不会有回调。 basicChannel.invokeMethod('jsLoadListener').then((value)会卡住。

  • 综上所属代码可能是写错了。