滴滴國際化項目 Android 端演進

wuhen781 6年前發布 | 36K 次閱讀 Android Android開發 移動開發 MapBox

這是今年下半年我在 CSDN 舉辦的移動開發者大會上的分享,前幾天朋友問起才想起來沒在博客上同步。記錄下分享的重點。

目前大家用滴滴 App 在美國是可以打車的,對的,不用下載新的 App,現在的滴滴 App 在美國打開就會自動顯示海外打車頁面。
國際化在技術上有一定的特殊性,主要包括:
滴滴國際化項目 Android 端演進

(1) 地圖

地圖作為滴滴客戶端重要的支持及基礎,而目前我們的友商都沒有海外的路網數據,國際化我們需要接入新的國外地圖提供商。

 

(2) 對接不同的運力

目前滴滴國際化是與海外投資的伙伴進行合作,比如美國打車跟 Lyft 合作。

 

(3) 漫游網絡

目前國際化的主要用戶場景還是國內用戶出國打車,這時用戶是用國內手機和運營商海外漫游接入網絡。
以上的三個特殊性決定著我們需要在技術上的差異,下面的分享也圍繞地圖模塊、漫游網絡、多業務接入項目演進進行分享。

 

1. 地圖

這塊主要包括兩大問題:(1) 地圖選型,(2) 地圖切換

1.1 地圖選型

滴滴是個重度依賴地圖的 App,而目前我們的友商及大部分國內地圖提供商都沒有海外的路網數據。
我們前期針對的場景是國內用戶海外打車,Google Map 依賴 Google Play Service,國內手機幾乎都沒有這個 Service,即便安裝了 Google Play Service 部分手機也無法運行,另外即便都有了,漫游網絡也不能訪問 Google Map,所以最靠譜的 Google Map 一開始便被排除。

 

另外國內有些 App 在海外用了 Google Map,不過是通過中轉下發地圖切片的方式完成的,我們對地圖各方面的要求都很高,所以也不合適。

 

這些都要求我們去找一個合適的國外地圖。
(1) 海外地圖選型考察點
滴滴國際化項目 Android 端演進
我們對地圖強依賴,有些定制需求,如:很多 Marker 并且添加后需要修改、畫圓并可以動態調整半徑等等

 

國外可用地圖數據源主要有 OpenStreetMap、Here、Tomtom,OpenStreetMap 是個開源的地圖數據源,類似維基百科的模式,所以數據很全很新,甚至超過 Google Map,但不可避免會有些臟數據,前期的話我們主要是針對大城市,OpenStreetMap 的數據可以滿足我們的需求。

 

因為涉及到異地跨時區溝通,所以我們希望技術支持力度夠大。

 

性能包括地圖啟動時間、渲染速度、前端響應速度、后端響應速度。

 

在開始國際化前,當時滴滴的安裝包就已經很大了,基本是國內主流 App 之首(當然現在滴滴 App 已經挺小了),所以我們希望新的地圖夠小。

 

(2) 海外地圖全面對比
滴滴國際化項目 Android 端演進
這次我們調研了 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 機制,所以最終放棄 Mapbox 而選擇 Nutiteq。
后話:經過過滴滴這次合作事件后,Mapbox 開始重視起國內市場,在國內成立了研發中心,溝通方便了很多,并且最新已經解決了上面 arm so 的問題。

 

1.2 地圖切換

用不了 Google Map 帶來一個要求,我們選擇的地圖必須支持多國家,并且在設計時要支持以后不同地圖任意切換。是的,即地圖和 App 弱依賴。針對這個問題我們設計了地圖隔離層。
總體設計如下:
滴滴國際化項目 Android 端演進
上圖第二層 MapSDK 是地圖的標準 API 層,App 只與此層打交道,標準層的 API 設計以 Google Map API 為標準。
第三層 Adapter 層是具體地圖到標準 API 的適配實現層。每個地圖都有個 Adapter,負責將地圖 API 轉換成標準 API。

 

將原來的 App 與三方地圖直接依賴改為 App 依賴表示標準 API 的 MapSDK 層,由 MapSDK 通過具體的 Adapter 調用三方 SDK,這樣地圖切換只需要替換依賴的 Adapter 即可,其他地方無需改動。

 

新的設計后編譯依賴關系如下:
滴滴國際化項目 Android 端演進
App 依賴 Map Adapter,Map Adapter 依賴我們的 MapSDK 和三方的 Map SDK。
當我們需要更換三方地圖 SDK 時,僅需更換對應的 Map Adapter 即可。對于 Android,build.gradle 中更換依賴即可。

 

1.3 新的地圖模塊設計的好處

(1) 解耦,切換成本低
這個上面已經介紹,再也不會因為換了地圖牽一發而動全身。
(2) 學習成本低
業務開發人員只需要熟悉標準 MapSDK API 即可,不用了解其他地圖的具體使用,時間成本降低。
(3) 通用
適用于所有 App,以后新增 App,可直接使用之前成型的 Adapter。

 

1.4 地圖切換實現的注意事項

(1) 所有 API 適配
理論上 MapSDK 應為地圖所有 API 最大集,實際可以根據情況先去做所需功能的定義和適配。

 

(2) 標尺
需要統一標尺,如縮放尺度、相同坐標系等。

 

(3) 未支持 API 處理
因為標準層的 MapSDK 是地圖功能最大集,所以不可避免某些三方地圖不支持 MapSDK 定義的功能。比如根據一組點縮放這個功能,其對應的 Adapter 在實現這個功能時如果是 Debug 模式則拋異常,Release 模式則空實現。

 

還有如 MapSDK 的 API 規范前面已經介紹過以 Google Map API 為標準。另 Adapter 有具體的開發規范要求。

 

2. 漫游網絡

前面介紹過我們初期針對的是國內用戶海外打車場景,這時用戶是用國內手機和運營商海外漫游接入網絡,所以需要針對網絡訪問進行優化。
一般漫游網絡流程如下圖:
滴滴國際化項目 Android 端演進
用戶由海外運營商接入國內運營商,再通過公網(有墻)訪問 Web。我們的服務器部署在 AWS 上,用戶海外漫游打車網絡流程如下圖:
滴滴國際化項目 Android 端演進
由于公網訪問 AWS 非常慢,我們添加了海外專線,優化后用戶海外漫游打車網絡流程如下圖:
滴滴國際化項目 Android 端演進
用戶先訪問到國內的中轉服務器,中轉服務器再通過海外專線訪問 AWS。

 

這個過程中客戶端要做的工作包括: (1) 拉取中轉服務器域名列表
(2) 使用中轉服務器域名列表中域名訪問,出錯則用原始域名降級重試
(3) 定時及推送更新域名列表
這里域名順序由服務端自己負載均衡,返回的中轉服務器域名列表是中轉服務器域名還是直接海外域名也由服務器決定。

 

3. Android 項目演進

3.1 原有模式

之前國際化業務的工程是很簡單的方式,所有業務、組件、工具放在一起,根據具體包名劃分:
滴滴國際化項目 Android 端演進
這個在早期問題不大,并且開發起來快速方便,但隨著更多業務接入,如我們前面說過的新的國家運力接入,問題就日益明顯,包括:
(1) 組件之間耦合
雖然已經劃分包名,但依然可以互相調用,組件間依賴關系不清,甚至有循環依賴。
(2) 添加新業務不便
(3) 開發問題
規模越來越大致提交沖突可能性變大。

 

3.2 SDK 工程提取

將原工程整體拆分為業務工程和 SDK 工程,單業務工程直接依賴 SDK,可獨立開發、獨立運行、獨立打包。如下:
滴滴國際化項目 Android 端演進
這樣在接入新的業務后,總體項目結構如下圖:
滴滴國際化項目 Android 端演進
每個業務作為單獨工程,共用組件、工具、業務統一到 SDK 層中。
集成工程負責集成 Lyft、Ola、GrabTaxi 項目,所有業務項目提供 AAR,由集成工程整體打包對外發布。

 

3.3 SDK 工程組件化拆分

為了解決組件之間耦合,防止后續問題加劇,同時方便協同開發和更好的復用,將 SDK 工程組件化拆分如下:
滴滴國際化項目 Android 端演進
SDK 整體拆分為 Business Library 和 Util Library 兩大部分,主要依據是是否可以獨立于我們業務,他們間不允許反向依賴。每個部分包含若干組件,每個組件都以 Module 形式存在。

 

Business Library 為通用業務層,包含通用業務組件,如平滑移動、上車點、定位、地理信息、打點、網絡封裝。 其中 CommonBusiness 存放暫時通用、但尚不足以作為一個單獨組件的公共業務,以后可能獨立出來,注意包名規范方便未來獨立。

 

Util Library 為工具庫,大致分為 View 和 Util,DidiSDK 為滴滴 App 整體通用組件包,包含通用的圖片緩存、網絡請求、基礎登陸組件等等。

 

3.4 SDK 組件化拆分后依賴關系圖

滴滴國際化項目 Android 端演進
通過上圖我們可以發現即便只是 Business Library 層,組件也根據依賴關系劃分為明顯的上下層。

 

3.5 SDK 組件化劃分事項

(1) 單一及開閉原則
每個模塊只代表一個功能模塊或一個公共業務,對于個性化或定制功能以接口形式對外開放。
PS:目前 CommonBusiness 模塊暫時作為國際化 SDK 整體集成打包的模塊,即國際化 SDK 項目中的 sdk Module,后續當其中某個公共業務足夠成為一個模塊時可繼續拆分出來。

 

(2) 拆分粒度
項目的演進是不斷進行的,沒必要將每個細小組件都拆分出來,這樣不僅增加了項目的復雜度,同時也會影響編譯時間。
先根據實際需要拆分必要的組件,太小暫不足以獨立的組件可以在以后不斷進行的重構中根據需要拆分。如上面的 CommonBusiness 模塊,當然需要保持一定的規范方便以后拆分。

 

(3) 依賴關系
通過依賴圖整理依賴關系,防止重復依賴,同時看出沉淀關系。
1. Util Library 不能反向依賴 Business Library;
2. Business Library 除了基礎部分,如 Net、Geo、EventTrack 外,其他部分盡量不要相互依賴;
3. Business Library 中 Net、Geo、EventTrack 不允許反向依賴其他模塊。

 

(4) 開發規范
為了保證擴展性及方便以后繼續拆分:
1. 所有業務包名以 com.didi.{xx}.sdk.{businessName} 開頭;
2. CommonUtil 模塊中所有工具包名以 com.didi.{xx}.sdk.util.{utilName} 開頭;
3. CommonView 模塊中所有 View 包名以 com.didi.{xx}.sdk.view.{viewName} 開頭;

 

(5) 組件間通信
放棄原來造成耦合嚴重的 EventBus,改用原生的通信方式,包括原生 (startActivityForResult) 、內部廣播、回調等。

 

3.6 SDK 組件化項目整體設計圖

滴滴國際化項目 Android 端演進
其中虛線部分為 SDK 層。

 

3.7 組件化拆分后的好處

(1) 組件間解耦
(2) 業務并行開發、測試
(3) 組件單獨測試

 

來自:http://www.trinea.cn/android/didi-internationalization-android-evolution/

 本文由用戶 wuhen781 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!