目前,在美国,人人都可以使用滴滴App打车。是的,无需下载新的应用程序。目前滴滴App在美国打开会自动显示海外打车页面。国际化在技术上有一定的特殊性,主要包括:(1)地图地图是滴滴客户端的重要支撑和基础,但目前我们的友商都没有海外路网数据。为了国际化,我们需要访问新的国外地图供应商。(2)对接不同运力目前,滴滴的国际化是与海外投资伙伴合作,比如AmericanTaxi和Lyft的合作。(3)目前漫游网络的主要用户场景是国内用户出国打车。此时用户使用国内手机和运营商漫游到海外接入网络。以上三个特殊性决定了我们需要的技术差异。下面的分享也重点介绍了地图模块、漫游网络、多业务接入项目的演进。1.地图主要包括两大问题:(1)地图选择,(2)地图切换1.1地图选择滴滴是一个非常依赖地图的APP,目前我们的朋友和国内大部分地图商都没有海外路网数据.我们前期针对的场景是国内用户在海外打车。Google地图依赖于GooglePlay服务。几乎所有的国产手机都没有这项服务。地图,所以最靠谱的谷歌地图一开始就被排除在外了。另外,国内部分APP在海外使用谷歌地图,不过是通过传递地图切片的方式完成的。我们对地图各方面的要求都很高,所以不太适合。这些都需要我们找到合适的国外地图。(1)国外地图选择和检查点我们对地图的依赖比较大,还有一些定制化的需求,比如:标记较多,添加后需要修改,画圈,动态调整半径等。可用的国外地图数据源主要有OpenStreetMap,Here,Tomtom,OpenStreetMap是一个开源的地图数据源,类似于维基百科的模式,所以数据非常的全和新,甚至超过了GoogleMap,但是难免会有一些脏数据。前期我们主要针对大城市,OpenStreetMap的数据可以满足我们。要求。因为涉及异地跨时间通信,所以希望技术支持足够强大。性能包括地图启动时间、渲染速度、前端响应能力、后端响应能力。在开始国际化之前,滴滴的安装包已经很大了,在国内主流APP中基本排在前列(当然现在滴滴APP已经很小了),所以希望新地图足够小。(二)海外地图综合对比本次我们调研了Mapbox、Nutiteq、Here、Tomtom、Bing等五家海外地图。其中,必应没有安卓版本;Tomtom的Android版本很老,但是功能太简单,几乎没有文档;HereSDK高达40M。跟他们沟通后,只能简化为25M。这个尺寸是我们绝对不能接受的;所以我们专注于集成和测试两个地图供应商,Mapbox和Nutiteq。Mapbox和Nutiteq的功能和性能都满足我们的需求,地图数据源主要是OSM(OpenStreetMap)。Mapbox的API设计与国内地图类似。接近GoogleMap,上手容易,而且整个SDK开源,地图样式更美观。不过Nutiteq的地图底层设计很独特,API的使用也很不一般,这也给我们的访问带来了很多麻烦。Mapbox的网页用户较多,包括访问量较高的Foursquare、Pinterest等,但安卓用户不多;Nutiteq安卓用户比较多,但是整体体量不是很大,但是我们没有更好的选择,我们前期的体量也不会很大,所以都在可以接受的范围内。综合来看,我们更喜欢Mapbox,但是Mapbox只能通过GitHubIssues和邮件来反馈问题,响应很慢;Nutiteq可以通过Skype进行交流,非常高效。为了保险起见,Mapbox和Nutiteq都做了全面的接入和测试,最终证明是有用的。和大多数App一样,为了让包更小,我们的主工程配置了abiFilter“armeabi”,只有armabi这样,而Mapbox的armeabiso不能在armv7机器上运行。在早期的集成测试中,我们修改了Gradle脚本编译copy的方式,这样就让测试通过了,但是Mapbox一直不愿意改,而且国内市场不支持谷歌的ApkSplits机制,所以最终放弃了Mapbox,选择了纽钛克。后记:最新版的Mapbox解决了这个问题,而且国内有相关的市场人员,沟通顺畅多了。1.2无法切换地图。GoogleMap带来了一个需求。我们选择的地图一定要支持多国,而且以后设计一定要支持不同地图之间的任意切换。是的,就是地图和app弱依赖。为了解决这个问题,我们设计了地图隔离层。整体设计如下:上图中的第二层MapSDK是地图的标准API层,App只和这一层打交道。标准层的API设计是基于GoogleMapAPI。第三层Adapter层,是将具体地图适配到标准API的实现层。每个地图都有一个Adapter,负责将地图API转换为标准API。将原有app与三方地图的直接依赖改为app依赖的MapSDK层来表示标准API,MapSDK通过特定的Adapter调用三方SDK,这样地图切换只需要替换掉依赖的Adapter,其他不需要改动。新的设计后编译依赖如下:App依赖MapAdapter,MapAdapter依赖我们的MapSDK和第三方MapSDK。当我们需要更换三方地图SDK时,只需要更换对应的MapAdapter即可。对于Android,只需替换build.gradle中的依赖项即可。1.3新地图模块设计的好处(1)解耦,切换成本低这个上面已经介绍过了,不会再因为换地图而影响全身。(2)学习成本低业务开发者只需熟悉标准的MapSDKAPI,无需了解其他地图的具体使用,减少时间成本。(3)普遍适用于所有App。如果以后添加Apps,可以直接使用之前形成的Adapter。1.4实现地图切换的注意事项(1)所有API的适配从理论上讲,MapSDK应该是地图所有API的第一套。在实践中,您可以先根据情况定义和适配需要的功能。(2)比例尺需要统一,比如缩放比例尺,同一坐标系等。(3)不支持API处理,因为标准图层的MapSDK是最新的一套地图函数,所以不可避免部分第三方地图不支持MapSDK定义的功能。比如缩放的功能是基于一组点的,那么对应的Adapter在实现该功能时如果是Debug模式就会抛出异常,而在Release模式则为空。还有一个API规范比如MapSDK,前面已经介绍过,以GoogleMapAPI为标准。此外,Adapter有特定的开发规范要求。2.漫游网络前面说了,我们最初的目标是国内用户的海外打车场景。此时用户使用国内手机和运营商漫游海外接入网络,需要优化网络接入。一般的漫游网络流程是这样的:用户从境外运营商接入国内运营商,再通过公网(翻墙)上网。我们的服务器部署在AWS上,用户海外漫游和打车的网络流程如下:由于公网访问AWS很慢,所以我们增加了海外专线,中转服务器通过海外专线访问AWS。客户端在这个过程中要做的工作包括:(1)拉取中转服务器的域名列表(2)使用中转服务器域名列表中的域名访问,如果出错,使用原域名降级重试(3)定期更新域名列表并推送到这里域名顺序由服务器自己负载均衡,返回的中转服务器域名列表是中转服务器域名还是直接海外域名也是由服务器决定的。3.Android项目的演变3.1在原始模式之前,国际化业务的项目非常简单。所有的业务、组件、工具都放在一起,按照具体的包名进行划分:这个在前期问题不大,开发起来又快又方便。但是随着业务接入的增多,比如我们前面提到的新的国家能力接入,问题也越来越明显,包括:(1)组件之间的耦合虽然已经分包名,但是仍然可以相互调用。依赖关系不明确,甚至存在循环依赖。(2)增加新服务不方便(3)开发问题规模越来越大,发生冲突的可能性越来越大。3.2SDK工程提取将原工程拆分为业务工程和SDK工程。单个业务项目直接依赖SDK,可独立开发、独立运行、独立打包。如下:接入新业务后,项目整体结构如下:每个业务都是一个独立的项目,将常用的组件、工具、服务统一到SDK层。集成工程负责集成Lyft、Ola和GrabTaxi项目。所有业务项目均提供AAR,由集成工程整体打包发布。3.3SDK项目组件化拆分,解决组件间的耦合,防止后续问题激化,同时方便协同开发,更好的复用。SDK项目的组件化是这样拆分的:大部分主要依据是他们能不能独立于我们的业务,他们之间不允许反向依赖。每个部分包含若干个组件,每个组件以Module的形式存在。业务库是公共业务层,包括公共业务组件,如平滑移动、登机点、定位、地理信息、管理、网络封装等。其中,CommonBusiness存放的是暂时通用但不足以作为单独组件的公共业务。将来可能会独立。注意包名规范,方便以后独立。UtilLibrary是一个工具库,大致分为View和Util。DidiSDK是滴滴App的整体通用组件包,包括通用图片缓存、网络请求、基本登录组件等。3.4SDK组件化拆分后的依赖图通过上图我们可以发现,即使只是业务库层,组件根据依赖关系分为明显的上下层。3.5SDK组件划分(1)单一和开放封闭原则每个模块只代表一个功能模块或一个公共服务,以接口的形式对外开放,实现个性化或定制化的功能。PS:目前CommonBusiness模块??暂时作为国际化SDK的集成封装模块,即国际化SDK工程中的sdk模块。后面某个共同的业务足够成为一个模块的时候,就可以继续拆分出来。(2)拆分粒度项目的演进是连续的,没有必要拆分每一个小组件,这样不仅增加了项目的复杂度,也影响了编译时间。先根据实际需要拆分必要的组件,太小无法独立的组件可以在以后的不断重构中根据需要进行拆分。像上面的CommonBusiness模块??,当然需要维护一定的规范,方便以后拆分。(3)Dependencies通过依赖图组织依赖关系,防止重复依赖,同时看到沉淀关系。1、Util库不能反向依赖Business库;2、业务库除Net、Geo、EventTrack等基础部分外,其他业务库部分尽量不相互依赖;3、业务库中的Net、Geo、EventTrack不允许反向依赖其他模块。(4)为了保证扩展性和便利性,开发规范将继续拆分:所有业务包名均以com.didi.{xx}.sdk.{businessName}开头;CommonUtil模块中所有工具包名称均以com.didi.{xx}.sdk.util.{utilName}开头;CommonView模块中所有的View包名都以com.didi.{xx}.sdk.view.{viewName}开头;(5)组件间通信摒弃耦合严重的EventBus,改用原生通信方式,包括原生(startActivityForResult)、内部广播、回调等。3.6SDK组件工程总体设计图,其中虚线部分是SDK层。3.7组件拆分后的好处(1)组件间解耦(2)业务开发测试并行(3)组件单独测试【本文为专栏作者Trinea原创文章,转载联系作者授权】点此查看该作者的更多好文章
