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
。
其他
项目地址
作者:吉哈达
链接:https://juejin.cn/post/7103349119481184287
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。