通过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);
}
}
}];
}
}];
打印如下:
iOS端参数前面替那家\a\u00000000这种字符 :
代码如下:
- (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:
dart:
android:
可以看到:这个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)会卡住。
- 综上所属代码可能是写错了。