当前位置: 首页 > news >正文

网站开发做什么简单重庆百度优化

网站开发做什么简单,重庆百度优化,广州中小企业网站建设,中山精品网站建设新闻导语 发布app后#xff0c;开发者最头疼的问题就是如何解决交付后的用户侧问题的还原和定位#xff0c;是业界缺乏一整套系统的解决方案的空白领域#xff0c;闲鱼技术团队结合自己业务痛点在flutter上提出一套全新的技术思路解决这个问题。 我们透过系统底层来捕获ui事件流…导语 发布app后开发者最头疼的问题就是如何解决交付后的用户侧问题的还原和定位是业界缺乏一整套系统的解决方案的空白领域闲鱼技术团队结合自己业务痛点在flutter上提出一套全新的技术思路解决这个问题。 我们透过系统底层来捕获ui事件流和业务数据的流动并利用捕获到的这些数据通过事件回放机制来复现线上的问题。本文先介绍flutter触摸手势事件原理接着介绍里面怎样录制flutter ui手势事件然后介绍怎样还原回放flutter ui手势事件最后附上包括native录制回放的整体框架图。为了便于理解本文读者可以先阅读我之前写的关于native录制和回放文章《千人千面线上问题回放技术》 背景 现在的app基本都会提供用户反馈问题的入口然而提供给用户反馈问题一般有两种方式 直接用文字输入表达或者截图直接录制视频反馈 这两种反馈方式常常带来以下抱怨 用户输入文字好费时费力开发1看不懂用户反馈说的是什么意思开发2大概看懂用户说的是什么意思了但是我线下没办法复现哈开发3看了用户录制的视频但是我线下没办法重现也定位不到问题 所以为了解决以上问题我们用一套全新的思路来设计线上问题回放体系 Flutter 手势基础知识 如果要录制和回放flutter ui事件那么我们首先必须了解flutter ui手势基本原理。 1. Flutter UI触摸原始数据Pointer 我们可以把Flutter中的手势系统分两层概念来理解。第一层概念为原始触摸数据(pointer)它描述了屏幕上指针例如触摸鼠标和触控笔的时间类型位置和移动。 第二层概念为手势描述由一个或多个原始移动数据组成的语义动作。一般情况下单独的原始触摸数据没有任何意义。 原始触摸数据是由系统传给nativenative再通过flutter view channel传给flutter。 flutter接收native传来的原始数据接口如下 void _handlePointerDataPacket(ui.PointerDataPacket packet) {// We convert pointer data to logical pixels so that e.g. the touch slop can be// defined in a device-independent manner._pendingPointerEvents.addAll(PointerEventConverter.expand(packet.data, ui.window.devicePixelRatio));if (!locked)_flushPointerEventQueue();} 2. Flutter UI碰撞测试 当屏幕接收到触摸时dart Framework会对您的应用程序执行碰撞测试以确定触摸与屏幕相接的位置存在哪些视图renderobject。 触摸事件然后被分发到最内部的renderobject上。 从最内部renderobject开始这些事件在renderobject树中向上冒泡传递通过冒泡传递最后把所有的renderobject遍历出来从这个传递机制可想而知遍历出来renderobject列表里的最后一个是WidgetsFlutterBinding严格来讲WidgetsFlutterBinding不是renderobject后面会介绍到WidgetsFlutterBinding。 void _handlePointerEvent(PointerEvent event) {assert(!locked);HitTestResult result;if (event is PointerDownEvent) {assert(!_hitTests.containsKey(event.pointer));result HitTestResult();hitTest(result, event.position);_hitTests[event.pointer] result;assert(() {if (debugPrintHitTestResults)debugPrint($event: $result);return true;}());} else if (event is PointerUpEvent || event is PointerCancelEvent) {result _hitTests.remove(event.pointer);} else if (event.down) {result _hitTests[event.pointer];} else {return; // We currently ignore add, remove, and hover move events.}if (result ! null)dispatchEvent(event, result);} 上面代码以 histTest()检测当前触摸 pointer event 涉及到哪些视图。 最后通过dispatchEvent(event, result)来处理该事件。 void dispatchEvent(PointerEvent event, HitTestResult result) {assert(!locked); assert(result ! null);for (HitTestEntry entry in result.path) {try {entry.target.handleEvent(event, entry);} catch (exception, stack) {}}} 上面的代码就是用来分别调用每个视图RenderObject的手势识别器独自处理当前触摸事件决定是否接收此事件。 entry.target是每个widget对应的RenderObject,所有的RenderObject都需要实现implementsHitTestTarget类的接口HitTestTarget里面有就有handleEvent这个接口所以每个RenderObject都需要实现handleEvent这个接口 这个接口就是用来处理手势识别。 abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget 除了最后一个WidgetsFlutterBinding外其他视图RenderObject调用自己的handleEvent来识别手势其作用就是判断当前手势是否要放弃如果不放弃则丢到一个路由器里这个路由器就是手势竞技场最后由WidgetsFlutterBinding 调用handleEvent统一决议这些手势识别器最终谁胜出所以这里WidgetsFlutterBinding.handleEvent其实就是统一处理接口它的代码如下 void handleEvent(PointerEvent event, HitTestEntry entry) {pointerRouter.route(event);if (event is PointerDownEvent) {gestureArena.close(event.pointer);} else if (event is PointerUpEvent) {gestureArena.sweep(event.pointer);}} 3. Flutter UI手势决议 从上面的介绍可以得出一次触摸事件可能触发多个手势识别器。框架通过让每个识别器加入一个“手势竞争场”来决议用户想要的手势。“手势竞争场”使用以下规则来决议哪个手势胜出非常简单 在任何时候任何识别器都可以自己宣布失败并主动离开“手势竞争场”。如果在当前“竞争场”中只剩下一个识别器那么剩下来的就是赢家赢家意味着独自接收此触摸事件并做出响应动作在任何时候任何识别器都可以自己宣布胜利并且最终就是它胜利所有剩下的其他识别器都会失败4. Flutter UI手势例子 下面示例表示屏幕window由ABCDEFKG视图组成其中A视图是根视图即是最底下的视图。红圈表示触摸点位置触摸落在G视图的中间位置。 根据碰撞测试遍历出响应此触摸事件的视图路径 WidgetsFlutterBinding — A — C — K — G (其中GKCA是renderObject) 遍历路径列表后开始调用各自的视图GKCAentry.target.handleEvent来把自己识别器放到竞技场里参加决议当然有些视图由于根据自己的逻辑判断主动放弃识别该触摸事件。这个处理过程如下图 按G-K-C-A-WidgetsFlutterBinding顺序分别调用handleEvent()方法最后通过WidgetsFlutterBinding调用自己的handleEvent()接口来统一决议最终哪个手势识别器胜出。 胜出的那个手势识别器通过回调方法回调到上层业务代码流程如下 Flutter UI录制 从上面的flutter手势处理可知我们只需要在手势识别器回调上包装回调方法即可拦截到手势回调方法这样我们就可以在拦截过程读到WidgetsFlutterBinding — A — C — K — G链路的这棵视图树。我们只需要把这个棵树树上的节点相关属性和手势类型记录下来那回放时通过这些信息去匹配到当前界面上的对应视图即可回放。下面是tap事件的录制代码其他类型手势的录制代码原理一样这里略过。 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();} 录制流程图如下 Flutter UI回放 ui回放分两部分第一部分通过录制的相关信息match到当前界面相应视图第二部分是在此视图上进行模拟相关手势动作这部分是个难点也是重点其中涉及到怎样生成原始的触摸数据信息里面有时间类型坐标方向如果这些信息设置不合理或者错误会导致crash还有滚动距离不符需要补偿怎么补偿等等。 下面是滚动事件回放流程图其他类型手势的回放原理一样。 上面的预处理识别消耗指的是在滚动开始时手势识别器要判断是否符合滚动手势所需要滚动的距离。 所以我们为了让其控件滚动首先要生成一些触摸点数据让手势识别器识别为滚动事件。这样才能进行后续的滚动动作。 下面是滚动处理逻辑代码如下 void verticalScroll(double dstPoint, double moveDis) {preReplayPacket null;if (0.0 ! moveDis) {//此处计算滚动方向和滚动单元像素偏移由于代码太长略过int count ((ui.window.devicePixelRatio * moveDis) / (unit.abs())).round() * 2;if (count minCount) {count minCount; //保证最少偏移50/225 小于这个数 可能没反应因为被其他控件检测滚动消耗掉了//还有就是如果count太小count被scroll view消耗完前并没有滚动这是就触摸结束了ui.PointerChange.up那可能引起cell//点击事件跳转事件}final double physicalX rect.center.dx * ui.window.devicePixelRatio; //376.0;double physicalY;final double needOffset (count * unit).abs();final double targetHeight rect.size.height * ui.window.devicePixelRatio;final int scrollPadding rect.height ~/ 4;if (needOffset targetHeight / 2) {physicalY rect.center.dy * ui.window.devicePixelRatio;} else if (needOffset targetHeight / 2 needOffset targetHeight) {physicalY (orgMoveDis 0)? (rect.bottom - scrollPadding) * ui.window.devicePixelRatio: (rect.top scrollPadding) * ui.window.devicePixelRatio;} else {physicalY (orgMoveDis 0)? (rect.bottom - scrollPadding) * ui.window.devicePixelRatio: (rect.top scrollPadding) * ui.window.devicePixelRatio;count ((rect.height - 2 * scrollPadding) *ui.window.devicePixelRatio /unit.abs()).round();}final Listui.PointerDataPacket packetList createTouchDataList(count, unit, physicalY, physicalX);exeScroolTouch(packetList,dstPoint);} else {new Timer(const Duration(microseconds: fpsInterval), () {replayScrollEvent();});}} 上面代码大概处理逻辑1.计算滚动方向每个生成的触摸数据偏移单元 2.计算滚动的开始位置 3.生成滚动原始触摸数据列表 4.循环发射原始触摸数据并计算是否滚动到指定的位置如果还达不到指定的位置则继续补给 生成滚动原始触摸数据列表代码如下 第一数据是down触摸数据其他都是move触摸数据。up数据在这里不需要生成当滚动距离到目标位置后才另外生成up触摸数据。为什么这样设计此处留给大家思考 Listui.PointerDataPacket createTouchDataList(int count,double unit,double physicalY,double physicalX){final Listui.PointerDataPacket packetList ui.PointerDataPacket[];int uptime 0;for (int i 0; i count; i) {ui.PointerChange change;if (0 i) {change ui.PointerChange.down;} else {change ui.PointerChange.move;physicalY unit;if (i 15) //前面几个点让在短时间内偏移的距离长点 这样避开单击和长按事件{physicalY unit;physicalY unit;}}uptime replayOnePointDuration;final ui.PointerData pointer new ui.PointerData(timeStamp: new Duration(microseconds: uptime),change: change,kind: ui.PointerDeviceKind.touch,device: 1,physicalX: physicalX,physicalY: physicalY,buttons: 0,pressure: 0.0,pressureMin: 0.0,pressureMax: touchPressureMax,distance: 0.0,distanceMax: 0.0,radiusMajor: downRadiusMajor,radiusMinor: 0.0,radiusMin: downRadiusMin,radiusMax: downRadiusMax,orientation: orientation,tilt: 0.0);final Listui.PointerData pointerList ui.PointerData[];pointerList.add(pointer);final ui.PointerDataPacket packet new ui.PointerDataPacket(data: pointerList);packetList.add(packet);}return packetList;} 循环发射原始触摸数据并判断是否继续补给代码如下 我们以定时器不断的往系统发送触摸数据每次发送数据前都需要判断是否已经达到目标位置。 void exeScroolTouch(Listui.PointerDataPacket packetList,double dstPoint){Timer.periodic(const Duration(microseconds: fpsInterval), (Timer timer) {final ScrollableState state element.state;final double curPoint state.position.pixels;//ui.window.physicalSize.height*state.position.pixels/RecordInfo.recordedWindowH;final double offset (dstPoint - curPoint).abs();final bool existOffset offset 1 ? true : false;if (packetList.isNotEmpty existOffset) {sendTouchData(packetList, offset);} else if (packetList.isNotEmpty) {record.succ true;timer.cancel();packetList.clear();if (null ! preReplayPacket) {final ui.PointerDataPacket packet createUpTouchPointPacket();if (null ! packet) {ui.window.onPointerDataPacket(packet);}}new Timer(const Duration(microseconds: fpsInterval), () {replayScrollEvent();});} else if (existOffset) {record.succ true;timer.cancel();packetList.clear();final ui.PointerDataPacket packet createUpTouchPointPacket();if (null ! packet) {ui.window.onPointerDataPacket(packet);}verticalScroll(dstPoint, dstPoint - curPoint);} else {finishReplay();}});} 问题回放整体框架图 下图包括native和flutter包括ui和数据。 总结 本文大概介绍了flutter ui手势问题回放核心部分由四部分组成一是flutter手势原理二是flutter ui录制三是flutter ui回放四是整个框架图由于篇幅有限这四分部都介绍比较笼统不够详细请谅解flutter录制回放代码其实很多我这里只是附上比较重要而且易于理解的代码。其他不重要或不易读懂的代码都省掉了。如果对里面的技术点感兴趣你可以关注我们的公众号。我们后续会单独对里面的技术点详细深入的分析发文。如果觉得上面有错误的地方请指出。谢谢 后续的深入 到目前为止我们现在的flutter ui录制回放已经开发完成但我们后续还需要继续优化和深入。我们后续从两个点来深入优化1.如何在回放时模拟的触摸事件更逼真比如滚动加速度一次的滚动其实是一个曲线变化的过程 2.解决手势录制和回放不一致性。举个例子在键盘里输入123我们录制时截获到了手势123但是由于业务上层的bug导致了当时输入3没有响应输入框里只显示12我们回放时模拟手势123最终回放完后输入框显示123所以这样导致录制和回放不一致性这个问题怎么解决这是个麻烦的问题我们后续会解决。而且已经有这解决方案。 原文链接 本文为云栖社区原创内容未经允许不得转载。
http://www.zqtcl.cn/news/954604/

相关文章:

  • 网络推广网站排行榜百度怎么搜索网址打开网页
  • 网站制作和如何推广深圳西乡
  • 男生女生做污事网站免费西安企业展厅设计公司
  • 做网络写手最好进那个网站网页建站需要多少钱
  • 网站打开不对摄影设计说明200字
  • 无锡网站制作公司排名网站开发与应用 大作业作业
  • 网站建设中搜索引擎wordpress 不在首页显示文章
  • 先做网站先备案嘉兴网站建设推广
  • 建设法律法规文本查询网站Html手机浏览网站变形
  • 怎么拥有个人网站wordpress做的网站
  • wordpress建什么站江苏网站建设效果
  • 建设网站网站多少钱东莞网站建设 光龙
  • 天津和平做网站哪家好搞笑网站建设目的和意义
  • 一般做网站带宽选择多大的wordpress页面侧菜单
  • 海淀青岛网站建设友情链接适用网站
  • 青海建设厅官方网站资阳seo
  • 网站个人备案 企业备案深圳高端网站建设网页设计
  • 网站广东省备案国产最好的a级suv88814
  • 没有公司怎么做网站西安市市政建设网站
  • 北京网站制作net2006装饰网站建设策划书
  • 建立什么网站中小学图书馆网站建设
  • 襄阳网站建设外包任县附近网站建设价格
  • led灯网站建设案例有没有什么东西可以做网站
  • 网站可视化设计企业网络管理系统
  • 优惠券怎做网站南宁网站公司
  • 灌南县规划局网站理想嘉苑规划建设那些公司做网站好
  • 大型网站开发的主流语言wordpress 连接flickr
  • 制作一个网站流程怎样做网站运营
  • 可以完成交易的网站 做微信公众号电商网站开发
  • 上海市建设安全协会官方网站机械加工怎么找客户