美团打车业务很早就在美团App与点评App中提供了服务入口,并在技术上采用了H5与Native的混合开发技术。随着业务上线,有用户反馈我们的地图性能有一些问题,原因是我们打车地图使用的是Web版的地图(通过腾讯地图JavaScript API),业内同类产品使用的是Native版的地图SDK,Native地图相比Web地图具有天然的性能优势,所以美团打车地图从首屏地图加载到后续的地图操作体验都有一定差距。
为了改善打车业务的地图体验,我们想到的方案是在展示地图的部分使用Native地图,而非地图部分使用H5页面来显示,这样既能追平与竞品的地图性能差距,又能充分发挥H5的开发效率。这种方案乍一看似乎是传统的Hybrid开发,没什么难度与新奇。比如地图使用预先内置到App中的地图SDK实现,H5与Native的交互使用业界成熟的JSBridge技术。但从打车业务角度来看,因为打车业务有很多功能入口需要漂浮在地图之上,如起终点卡片、用户中心入口等,这种漂浮功能在技术上并不容易实现,而且还要保证用户触摸动作在漂浮元素与地图上发生时,分别派发给各自的事件系统,Hybrid技术在这方面没有经验可以借鉴。
带着这些挑战,我们进行一系列的尝试与试验,最终将问题解决并封装出我们打车业务的地图调用框架,我们称之为Native地图与Web融合框架(下文简称融合框架)。在这个过程中,我们总结出了一些经验,希望能给从事相关研究的同学带来一些帮助。
基于混合技术开发体系,我们研究了市面上大部分H5页面与Native地图的应用场景,主要分为如下两类:
经过分析后,我们发现这两种形式都无法满足打车业务场景的需求,因为目前市面上主流的打车业务场景由4部分构成,如下图所示:
上文第一类,H5页面与Native地图分别位于两个独立页面中,只能满足部分地图场景的需求,无法布局为上图H5与地图同框显示的效果。
上文第二类,实现这样的布局需要多个WebView才能实现,存在如下缺点:
调研结论是:市面上现存技术都无法满足打车场景的需求。
基于打车场景的特殊性,我们做了一个大胆的假设:把页面分为2层,下层是Native地图层,布满屏幕;上层是WebView层,完全覆盖到Native地图层之上,如下图所示:
我们期望的效果是:
具体实现思路有如下几点,参照下图:
为了验证想法是否正确,我们首先通过Android平台开发出Demo,验证这种分层智能传递消息的做法是可行的,该方案最大优点是兼顾了H5的开发效率与Native地图的高性能特性,非常符合美团业务地图场景的需求。为了让想法落地时更规范、更系统,我们进行了如下的框架设计。
先介绍一个“热区数据”的概念,下图(3.2节)在手势分发层存在着消息分发热区数据部分,下文简称热区数据。热区数据是针对上层WebView的一个概念,只对WebView层有效,对下层Native地图层无效。如果用户点击屏幕事件想让H5来捕获处理,可以在屏幕区域内设置一个逻辑上的矩形区域,如:[0, 0, 50, 50](上图左上角区域),这个数据被称为热区数据。
我们通过编写代码逻辑,控制手势消息分发的策略,如果手势消息发生在热区数据矩形范围内,我们把消息发送给WebView处理,否则发送给Native地图处理。如上图所示,可以在同一屏幕内设定多个热区,[0, 0, 50, 50]、[430, 0, 50, 50]、[0, 200, 480, 200],热区的格式可以自己定义,我们这里采用的基于WebView组件左上角为原点的像素坐标格式:[left, top, width, height]。
手势消息分发给WebView层流程
主要为上图1-->2-->3-->4过程,如下:
手势消息分发给Native地图层流程
主要为上图 5-->6-->7过程,如下:
热区数据的动态更新策略
因为打车业务底部的面板高度是可伸缩的,所以底部的热区数据并不是静止不动的,需要考虑热区数据也要随着DOM元素的拉伸做同步调整。可以通过在WebView H5层监控DOM的变化,DOM元素发生变化时,获取变化后的DOM元素位置、大小,格式化为热区数据,并更新到消息分发热区数据部分。因为拉伸动作是一个连续的动画效果,为了高效,我们只在动画结束的那一刻更新热区数据,中间过渡期不做处理。此整体流程为:2-->3-->4。
这部分功能需要Native端同学实现,包括iOS与Android。两端分别在启动App时设置三层内容,最上层是手势触摸事件接收层,中间是WebView层(背景设置透明),最下层是Native地图层(如腾讯地图SDK)。用数组记录当前热区数据,当手势分发层有事件发生时,通过Touch事件获取手指位置信息,遍历热区数组判断手指位置是否与热区的矩形相交,如相交则将消息分发给WebView层,否则分发给Native层。下边是Android与iOS消息分发关键代码:
Android分发层关键代码
@Override public boolean dispatchTouchEvent(MotionEvent event){ if(event.getAction() == MotionEvent.ACTION_DOWN) { // 分发层接收到手势触摸消息,通过dispatchService类判断手势是否落在热区内,从而确定消息分发的对象 this.touchHandler = dispatchService.inRegion(event) ? TouchHandler.WebView : TouchHandler.MapView; } // 分发给Native地图层 if(this.touchHandler == TouchHandler.MapView) { return this.mapView.dispatchTouchEvent(event); } // 分发给WebView H5层 return super.dispatchTouchEvent(event); }
iOS分发层关键代码
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { UIView *hitTestView = nil; // 分发层接收到手势触摸消息,通过pointInHotspot判断手势是否落在热区内,从而确定消息分发的对象 if ([self pointInHotspot:point]) { // 分发给WebView H5层 hitTestView = [self.WebView hitTest:point withEvent:event]; }else{ // 分发给Native地图层 hitTestView = [self.mapView hitTest:point withEvent:event]; } return hitTestView; }
该层有2个功能:
通讯桥即JSBridge技术,主要实现H5与Native的信息交互,这方面的技术都已比较成熟,业界有非常多的JSBridge实现,原理也都类似,常见的有:原生对象注入到H5层、URL拦截技术,Native调用JS常用的内置函数stringByEvaluatingJavaScriptFromString等。美团内部有比较成熟的KNB框架,所以项目中直接使用了KNB框架。
该层在地图SDK(如腾讯地图SDK)基础上进行了封装,提供一些打车业务友好的接口,如地图基本操作、打车起终点Marker添加、接送驾司机小车动画、地图事件、各种Marker的信息弹窗等。
打车业务前端的技术栈是: Vue + VueX + Vue-Router构建的单页系统。如下图所示,页面中存在很多H5元素需要添加热区,逐个元素编写代码添加的话会很繁琐,而且页面元素的位置、大小变化时还需要同步更新热区数据,这里我们使用了Vue中的directive(指令)来解决了此问题。
以上左右2图是用户操作时页面展示的不同状态,很明显右图底部卡片变高了,卡片变化同时需要同步更新对应的热区数据,directive技术可以很方便解决此问题,原理如下:
调试工具使用模拟器、真机都可以,开发期间我们使用的模拟器开发,测试期间QA使用真机验证。调试过程中主要验证2部分功能,分别是热区的验证与地图接口验证。
热区验证
主要验证主页面设置的热区是否正确,包括是否可以点击、底部卡片是否能正常拖拉、业务功能是否正常等。因为热区数据是一串数字,形如:[0, 0, 50, 50],无法直观判断出该数据是否有误,于是我们开发了一个可视化工具,将设置热区的元素都用红色矩形高亮显示,如下图所示,这样就能快速诊断出热区数据是否有异常。工具是使用Canvas画布实现的,画布大小与屏幕大小完全重合,借助画布就可以将矩形热区数据在屏幕中实时绘制出来。
地图接口验证
主要是编写单元测试完成的,本项目封装了50多个地图接口,每个接口都编写单测用例,观察入参、出参、控制台输出结果,地图展示效果是否正确等。测试主要在iOS模拟器中完成,这样方便在控制台打印一些调试信息进行诊断。
该框架在大众点评App中上线后地图体验明显提升,主要有体现在以下几个方面:
Native地图层代码接口稳定、功能丰富,基本满足地图场景的业务需求。只需首次跟版发布,后续只需要迭代H5的业务逻辑即可。
本文将WebView与Native地图组件叠加到一起,实现了用户手势事件智能分发的机制,解决了WebView与Native地图在同一页面内布局困难的问题。这种融合机制为打车业务提升迭代效率同时保障地图体验提供了一种有效的途径,日常业务功能上线采用H5技术迭代,Native地图作为不常更新的基础能力首次发版时安装到用户手机上,实现业务需求随时发版的能力,不再受各大应用商店的限制,用户操作地图的体验也更加流畅。该融合框架适合以下业务场景:
美团打车技术部终端研发中心,加鹏、张斌、杨睿、邱博、海峰等。
美团打车技术部终端研发中心诚招高级前端开发工程师、前端开发专家、高级iOS工程师、高级Android工程师。我们为美团点评用户提供优质的打车服务,是本地生活吃喝玩乐行的重要环节。欢迎各位小伙伴的加入,共同打造极致出行产品。感兴趣的同学可投递简历至:tech@meituan.com(邮件标题注明:美团打车技术部终端研发中心)
想阅读更多技术文章,请关注美团技术团队(meituantech)官方微信公众号。