本文共 2682 字,大约阅读时间需要 8 分钟。
发布app后,开发者最头疼的问题就是如何解决交付后的用户侧问题的还原和定位。传统的用户反馈方式主要有文字输入或视频录制两种,但都存在效率低、复现困难等问题。为了解决这些痛点,闲鱼技术团队结合自身业务需求,在Flutter平台上提出了一套全新的技术方案,实现了用户反馈问题的全流程解决。
目前的app普遍提供用户反馈入口,但现有反馈方式存在以下问题:
针对这些问题,我们设计了一套全新的用户反馈问题回放体系,通过抓取UI事件流和业务数据,利用事件回放机制实现问题复现。
要实现UI手势事件的录制与回放,首先需要理解Flutter手势系统的基本原理。
Flutter中的手势系统可以分为两层概念:
Flutter接收Native传来的原始数据通过以下接口:
void _handlePointerDataPacket(ui.PointerDataPacket packet) { _pendingPointerEvents.addAll(PointerEventConverter.expand( packet.data, ui.window.devicePixelRatio)); if (!locked) _flushPointerEventQueue();} Flutter通过“手势竞争场”机制来决议手势事件,最终确定接收触摸事件的控件。决议规则包括:
为了实现手势事件的录制,我们需要在手势识别器回调上拦截事件。通过分析视图树(WidgetsFlutterBinding → RenderObject树),可以获取到所有参与手势处理的控件信息。以下是手势录制的核心实现:
static GestureTapCallback onTapWithRecord(GestureTapCallback orgOnTap, BuildContext context) { if (null != orgOnTap && null != context) { final GestureTapCallback onTapWithRecord = () { if (bStartRecord) { saveTapInfo(context, TouchEventUIType.OnTap, null); } if (null != orgOnTap) { orgOnTap(); } }; return onTapWithRecord; } return orgOnTap;}static void saveTapInfo(BuildContext context, TouchEventUIType type, Offset point) { if (null == point && null != pointerPacketList && pointerPacketList.isNotEmpty) { final ui.PointerDataPacket last = pointerPacketList.last; if (null != last && null != last.data && last.data.isNotEmpty) { final ui.Rect rect = QueReplayTool.getWindowRect(context); point = new Offset( last.data.last.physicalX / ui.window.devicePixelRatio - rect.left, last.data.last.physicalY / ui.window.devicePixelRatio - rect.top); } } final RecordInfo record = createTapRecordInfo(context, type, point); if (null != record) { FlutterQuestionReplayPlugin.saveRecordDataToNative(record); } clearPointerPacketList();} 手势回放分为两部分:
滚动事件回放需要考虑以下关键点:
整体框架图展示了Native与Flutter的协同工作流程,包括:
本文介绍了Flutter手势事件录制与回放的核心技术,包括手势原理、录制实现、回放逻辑及整体架构。通过这套解决方案,开发者可以快速定位用户反馈问题,提升问题复现效率,降低开发成本。
后续优化方向包括:
如需了解更多技术细节,可以关注我们的公众号获取最新动态。如有建议或意见,欢迎在评论区留言。
转载地址:http://svgb.baihongyu.com/