注册

Flutter——平台通信记录器 : channel_observer

前言


Flutter自身的定位,决定了基于其开发的项目在不断迭代的过程中,会有越来越多的平台通信。这些通信多来自各种平台端的sdk,而这些sdk一般是由不同人、团队甚至公司负责的,所以在sdk变动过程中,可能由于沟通不够及时、或者疏忽大意而未能及时通知到客户端。


例如,某个字段类型由int变为string,如果这个字段涉及到核心业务线那么可能会在测试中及时发现,而如果是在非核心业务线则不一定能及时发现。 这种错误,在抵达flutter侧时多为TypeCast Error。 初期, 我们的APM 会将此类错误进行上报,但是由于platform channel众多,很难确定是由哪个channel引起的,为此我们增加了channel observer用于记录最近n条的平台通信记录。 当APM再次上报类似错误后,会导出channel记录一同上报,藉此便可排查出bug点。


下面我简单的介绍一下具体原理与实现。


原理与实现


Flutter-平台通信简介


Flutter与平台端的通信连接层位于ServicesBinding中,其主要负责监听平台信息(系统/自定义)并将其转到defaultBinaryMessenger中处理,其内部初始化方法:


mixin ServicesBinding on BindingBase, SchedulerBinding {
@override
void initInstances() {
super.initInstances();
_instance = this;
_defaultBinaryMessenger = createBinaryMessenger();

//...无关代码
}

@protected
BinaryMessenger createBinaryMessenger() {
return const _DefaultBinaryMessenger._();
}
}

通过createBinaryMessenger 方法,创建了一个_DefaultBinaryMessenger对象,Flutter平台端通信都由此类来负责,其内部实现如下:


class _DefaultBinaryMessenger extends BinaryMessenger {
const _DefaultBinaryMessenger._();

///当我们在调用 xxxChannel.invokeMethod()方法时,最终会调用到send()方法,
@override
Future<ByteData?> send(String channel, ByteData? message) {
final Completer<ByteData?> completer = Completer<ByteData?>();

///channel : 通道名
///message : 你的参数
///通过engine中转到平台端
ui.PlatformDispatcher.instance.sendPlatformMessage(channel, message, (ByteData? reply){
try {
///reply : 平台端返回的结果
completer.complete(reply);
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: ErrorDescription('during a platform message response callback'),
));
}
});
return completer.future;
}

///此方法与上面的 send 方法相对应,是服务于平台端调用flutter的方法。
///
///当我们通过方法 :
/// channel.setMethodCallHandler(xxHandler)
///在flutter侧对 channel绑定一个回调用于处理平台端的调用时,
///最终会转到此方法。
///
///通过channelBuffers,会记录下你的channel name以及对应的handler,
///当平台端调用flutter方法时,会查找对应channel的handler并执行。
@override
void setMessageHandler(String channel, MessageHandler? handler) {
if (handler == null) {
ui.channelBuffers.clearListener(channel);
} else {
ui.channelBuffers.setListener(channel, (ByteData? data, ui.PlatformMessageResponseCallback callback) async {
ByteData? response;
try {
response = await handler(data);
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: ErrorDescription('during a platform message callback'),
));
} finally {
callback(response);
}
});
}
}
}

通过上面的了解我们便知道了入手点:只需增加一个_DefaultBinaryMessenger的代理类即可。


实现


首先,我们需要自定义WidgetsFlutterBinding以混入我们自定义的ServicesBinding :


class ChannelObserverBinding extends WidgetsFlutterBinding with ChannelObserverServicesBinding{
static WidgetsBinding ensureInitialized() {
if(WidgetsBinding.instance == null) {
ChannelObserverBinding();
}
return WidgetsBinding.instance!;
}
}

随后我们在自定义的ServicesBinding中,添加我们的代理类BinaryMessengerProxy


mixin ChannelObserverServicesBinding on BindingBase, ServicesBinding{

late BinaryMessengerProxy _proxy;

@override
BinaryMessenger createBinaryMessenger() {
_proxy = BinaryMessengerProxy(super.createBinaryMessenger());
return _proxy;
}
}

这样我们就可以在代理类中,对平台通信进行记录了:


class BinaryMessengerProxy extends BinaryMessenger{

BinaryMessengerProxy(this.origin);

///....省略代码

@override
Future<void> handlePlatformMessage(String channel, ByteData? data, PlatformMessageResponseCallback? callback) {
return origin.handlePlatformMessage(channel, data, callback);
}

///这里我们对flutter的调用做记录
@override
Future<ByteData?>? send(String channel, ByteData? message) async {
//记录channel通信
final ChannelModel model = _recordChannel(channel, message, true);
if(model.isAbnormal) {
return origin.send(channel, message);
}
final ByteData? result = await origin.send(channel, message);
_resolveResult(model, result);
return result;
}

///这里我们可以对平台端的调用做记录
/// * 对MessageHandler增加一个代理即可。
@override
void setMessageHandler(String channel, MessageHandler? handler) {
origin.setMessageHandler(channel, handler);
}

}

效果图


当我们捕捉到TypeCast error时,就可以将异常堆栈及channel的通信记录一同上传。开发同学便可借助堆栈信息和调用记录,定位到具体的异常channel


其他


项目地址


channel_observer_of_kit


作者:吉哈达
链接:https://juejin.cn/post/7103349119481184287
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册