滴滴国际化项目 Android 端架构演进与详解

相关阅读:前端

吊炸天!74款APP完整源码!
android

2016年未,腾讯,百度,华为,搜狗和滴滴Android面试题汇总
git

Android N(7.0) 被美翻的新特性最全总结!github


滴滴国际化目前有着一些不一样于国内打车的特殊场景——国内用户拿着国产手机出国打车。国内地图、Google 地图均无法用;手机移动漫游网络太慢;同时须要对接不一样合做公司的司机运力,这是国际化客户端项目面临的主要问题。本文为滴滴出行技术专家 吴更新在 MDCC 2016 移动开发者大会上的演讲,主要介绍了滴滴国际化在地图选型、地图扩展适配、网络相关优化、项目总体技术拆分、演进方面的经验。面试

PPT 下载地址:https://github.com/MDCC2016/Android-Session-Slides。
视频观看地址:http://edu.csdn.net/course/detail/3094后端

目前你们用滴滴 App 在美国能够直接打车,不用下载新的 App,如今的滴滴 App 在美国打开就会自动显示海外打车页面。可是,国际化在技术上有必定的特殊性,滴滴国际化业务主要服务于国内用户在国外打车的场景,所以会涉及到与国内业务不太相同的地方,主要体如今地图、网络、运力来源三个方面。前二者很好理解,运力来源中的运力主要是指司机。在国内,我的能够注册滴滴司机,可是在国外咱们是与合做伙伴合做,由合做方提供运力,因此在不一样的国家,会与不一样的合做伙伴实现接入。具体以下:设计模式

  1. 地图缓存

    地图做为滴滴客户端重要的支持及基础,而目前咱们的友商都没有海外的路网数据,国际化咱们须要接入新的国外地图提供商。性能优化

  2. 对接不一样的运力服务器

    目前滴滴国际化是与海外投资的伙伴进行合做,好比美国打车跟 Lyft 合做。

  3. 漫游网络

    目前国际化的主要用户场景仍是国内用户出国打车,这时用户是用国内手机和运营商海外漫游接入网络。

以上的三个特殊性决定着咱们须要在技术上的差别,接下来将围绕地图模块、漫游网络、多业务接入项目演进详细展开。

地图

这部分主要包括两大问题:地图选型、地图切换。

1. 地图选型

滴滴是个重度依赖地图的 App,而目前咱们的友商及大部分国内地图提供商都没有海外的路网数据。咱们前期针对的场景是国内用户海外打车,Google Map 依赖 Google Play Service,国内手机几乎都没有这个 Service,即使安装了 Google Play Service 部分手机也没法运行,另外即使都有了,漫游网络也不能访问 Google Map,因此最靠谱的 Google Map 一开始便被排除。

另外国内有些 App 在海外用了 Google Map,不过是经过中转下发地图切片的方式完成的,但滴滴对于地图各方面的要求都很高,咱们必须找到一个合适的国外地图。

(1) 海外地图选型考察点

咱们对地图强依赖,有些定制需求,如:不少 Marker 而且添加后须要修改、画圆并能够动态调整半径等等。

国外可用地图数据源主要有 OpenStreetMap、Here、Tomtom,OpenStreetMap 是个开源的地图数据源,相似维基百科的模式,因此数据很全很新,甚至超过 Google Map,但不可避免会有些脏数据,前期的话主要是针对大城市,OpenStreetMap 的数据能够知足需求。但由于涉及到异地跨时区沟通,因此但愿技术支持力度够大。

性能方面则包括地图启动时间、渲染速度、前端响应速度、后端响应速度。

在开始国际化前,当时滴滴的安装包就已经很大了,基本是国内主流 App 之首(固然如今滴滴 App 已经挺小了),因此咱们但愿新的地图够小。

(2) 海外地图全面对比

此次咱们调研了 Mapbox、Nutiteq、Here、Tomtom、Bing 共五款海外地图。其中:

  • Bing 没有 Android 版;

  • Tomtom 有很古老的 Android 版,但功能过于简单,文档又几乎没有;

  • Here SDK 高达 40M,与他们沟通后,精简也只能到 25M,这个大小是绝对接受不了的;

因此咱们重点集成和测试的是 Mapbox 和 Nutiteq 这两家地图供应商。

Mapbox 和 Nutiteq 的功能和性能都知足咱们需求,地图数据源也都是以 OSM(OpenStreetMap) 为主。Mapbox 的 API 设计和国内地图相似,都是向 Google Map 靠拢,因此上手简单,而且整个 SDK 都是开源的,地图的样式也更美观些,而 Nutiteq 的地图底层设计比较独特,API 用法很不寻常,这也给咱们接入带来了很大的麻烦。

Mapbox 有众多的 Web 用户,包括访问量都不低的 Foursquare、Pinterest 等,但 Android 端用户并很少;Nutiteq 的 Android 用户多些,但总体量也不是很大,不过咱们并无更好的选择,并且前期咱们的量也不会很大,因此他们都在可接受范围内。

综合下来看的话,咱们是更倾向于 Mapbox,不过 Mapbox 只能经过 GitHub Issues 和邮件反馈问题,反应很慢;Nutiteq 能够 Skype 沟通,效率很高。为了保险起见,Mapbox 和 Nutiteq 都作了全面接入和测试,最终证实这样是有用的。

跟多数 App 同样,为了使得包更小,咱们的主工程配置了 abiFilter “armeabi”,仅打 armabi 的 so,而 Mapbox 的 armeabi so 没法跑在 armv7 机器上,前期集成测试咱们经过修改 Gradle 脚本在编译时 copy so 的方式让测试经过,而 Mapbox 一直不肯意改,国内市场又不支持 Google 的 Apk Splits 机制,因此最终放弃选择 Nutiteq。

后话:Mapbox 最新版已经解决了这个问题,并且国内有相关的市场人员,沟通起来也顺畅多了。

2. 地图切换

用不了 Google Map 带来一个要求,咱们选择的地图必须支持多国家,而且在设计时要支持之后不一样地图任意切换。是的,即地图和 App 弱依赖。针对这个问题咱们设计了地图隔离层。整体设计以下:

上图第二层 MapSDK 是地图的标准 API 层,App 只与此层打交道,标准层的 API 设计以 Google Map API 为标准。

第三层 Adapter 层是具体地图到标准 API 的适配实现层。每一个地图都有个 Adapter,负责将地图 API 转换成标准 API。

将原来的 App 与三方地图直接依赖改成 App 依赖表示标准 API 的 MapSDK 层,由 MapSDK 经过具体的 Adapter 调用三方 SDK,这样地图切换只须要替换依赖的 Adapter 便可,其余地方无需改动。

新的设计后编译依赖关系以下:


App 依赖 Map Adapter,Map Adapter 依赖咱们的 MapSDK 和三方的 Map SDK。当咱们须要更换三方地图 SDK 时,仅需更换对应的 Map Adapter 便可。对于 Android,build.gradle 中更换依赖便可。

3. 新的地图模块设计的好处

  1. 解耦,切换成本低

    这个上面已经介绍,不再会由于换了地图牵一发而动全身。

  2. 学习成本低

    业务开发人员只须要熟悉标准 MapSDK API 便可,不用了解其余地图的具体使用,时间成本下降。

  3. 通用

    适用于全部 App,之后新增 App,可直接使用以前成型的 Adapter。

4. 地图切换实现的注意事项

  1. 全部 API 适配

    理论上 MapSDK 应为地图全部 API 最大集,实际能够根据状况先去作所需功能的定义和适配。

  2. 标尺

    须要统一标尺,如缩放尺度、相同坐标系等。

  3. 未支持 API 处理

    由于标准层的 MapSDK 是地图功能最大集,因此不可避免某些三方地图不支持 MapSDK 定义的功能。好比根据一组点缩放这个功能,其对应的 Adapter 在实现这个功能时若是是 Debug 模式则抛异常,Release 模式则空实现。

还有如 MapSDK 的 API 规范前面已经介绍过以 Google Map API 为标准。另 Adapter 有具体的开发规范要求。

漫游网络

前面介绍过咱们初期针对的是国内用户海外打车场景,这时用户是用国内手机和运营商海外漫游接入网络,因此须要针对网络访问进行优化。

通常漫游网络流程以下图:

用户由海外运营商接入国内运营商,再经过公网(有墙)访问 Web。咱们的服务器部署在 AWS 上,用户海外漫游打车网络流程以下图:

因为公网访问 AWS 很是慢,咱们添加了海外专线,优化后用户海外漫游打车网络流程以下图:

用户先访问到国内的中转服务器,中转服务器再经过海外专线访问 AWS。

这个过程当中客户端要作的工做包括:

  • 拉取中转服务器域名列表

  • 使用中转服务器域名列表中域名访问,出错则用原始域名降级重试

  • 定时及推送更新域名列表
    这里域名顺序由服务端本身负载均衡,返回的中转服务器域名列表是中转服务器域名仍是直接海外域名也由服务器决定。

Android 项目演进

1. 原有模式

以前国际化业务的工程是很简单的方式,全部业务、组件、工具放在一块儿,根据具体包名划分:

这个在早期问题不大,而且开发起来快速方便,但随着更多业务接入,如咱们前面说过的新的国家运力接入,问题就日益明显,包括:

  • 组件之间耦合
    虽然已经划分包名,但依然能够互相调用,组件间依赖关系不清,甚至有循环依赖。

  • 添加新业务不便

  • 开发问题
    规模愈来愈大体提交冲突可能性变大。

2. SDK 工程提取

将原工程总体拆分为业务工程和 SDK 工程,单业务工程直接依赖 SDK,可独立开发、独立运行、独立打包。以下:

这样在接入新的业务后,整体项目结构以下图:

每一个业务做为单独工程,共用组件、工具、业务统一到 SDK 层中。集成工程负责集成 Lyft、Ola、GrabTaxi 项目,全部业务项目提供 AAR,由集成工程总体打包对外发布。

3. SDK 工程组件化拆分

为了解决组件之间耦合,防止后续问题加重,同时方便协同开发和更好的复用,将 SDK 工程组件化拆分以下:

SDK 总体拆分为 Business Library 和 Util Library 两大部分,主要依据是是否能够独立于咱们业务,他们间不容许反向依赖。每一个部分包含若干组件,每一个组件都以 Module 形式存在。

Business Library 为通用业务层,包含通用业务组件,如平滑移动、上车点、定位、地理信息、打点、网络封装。其中 CommonBusiness 存放暂时通用、但尚不足以做为一个单独组件的公共业务,之后可能独立出来,注意包名规范方便将来独立。Util Library 为工具库,大体分为 View 和 Util,DidiSDK 为滴滴 App 总体通用组件包,包含通用的图片缓存、网络请求、基础登录组件等等。

4. SDK 组件化拆分后依赖关系图

经过上图咱们能够发现即使只是 Business Library 层,组件也根据依赖关系划分为明显的上下层。

5. SDK 组件化划分事项

(1) 单一及开闭原则

每一个模块只表明一个功能模块或一个公共业务,对于个性化或定制功能以接口形式对外开放。

注:目前 CommonBusiness 模块暂时做为国际化 SDK 总体集成打包的模块,即国际化 SDK 项目中的 sdk Module,后续当其中某个公共业务足够成为一个模块时可继续拆分出来。

(2) 拆分粒度

项目的演进是不断进行的,不必将每一个细小组件都拆分出来,这样不只增长了项目的复杂度,同时也会影响编译时间。

先根据实际须要拆分必要的组件,过小暂不足以独立的组件能够在之后不断进行的重构中根据须要拆分。如上面的 CommonBusiness 模块,固然须要保持必定的规范方便之后拆分。

(3) 依赖关系

经过依赖图整理依赖关系,防止重复依赖,同时看出沉淀关系。

  • Util Library 不能反向依赖 Business Library;

  • Business Library 除了基础部分,如 Net、Geo、EventTrack 外,其余部分尽可能不要相互依赖;

  • Business Library 中 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) 、内部广播、回调等。

6. SDK 组件化项目总体设计图

其中虚线部分为 SDK 层。

7. 组件化拆分后的好处

  • 组件间解耦

  • 业务并行开发、测试

  • 组件单独测试


关于Java和Android大牛频道

Java和Android大牛频道是一个数万人关注的探讨Java和Android开发的公众号,分享和原创最有价值的干货文章,让你成为这方面的大牛!

咱们探讨android和Java开发最前沿的技术:android性能优化 ,插件化,跨平台,动态化,加固和反破解等,也讨论设计模式/软件架构等。由群来自BAT的工程师组成的团队

关注即送红包,回复:“百度” 、“阿里”、“腾讯” 有惊喜!!!关注后可用入微信群。群里都是来自百度阿里腾讯的大牛。

欢迎关注咱们,一块儿讨论技术,扫描和长按下方的二维码可快速关注咱们。搜索微信公众号:JANiubility。

公众号:JANiubility