服務24億級用戶App的大前端實踐

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"茄子科技(海外 SHAREitGroup)是國內出海的先行者,2015 年成立後就“走出去”。2017 年,主要產品 SHAREit(國內茄子快傳)全球用戶總量超 12 億,成爲新興市場的“國民應用”。截止目前,茄子科技產品矩陣全球累計安裝用戶量近 24億,覆蓋 200 多個國家和地區,涵蓋全球 45 種語言。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"服務全球幾十億用戶,茄子科技如何提升 App 的用戶體驗?怎樣解決 App 的崩潰問題?如何應對海外複雜的網絡問題?...... 針對上述問題,InfoQ 記者採訪了茄子科技前端負責人。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"提供高品質用戶體驗"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用戶體驗是 App 應用的核心追求之一。在所有用戶體驗問題中,崩潰屬於最高優先級。一旦出現崩潰,就意味着 App 徹底不可用,這會給用戶體驗造成非常大的傷害。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"解決應用崩潰問題"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"據茄子科技前端負責人介紹,爲解決崩潰問題,他們自建了一個 APM 平臺,這樣可以快速發現問題和協助定位問題。APM 平臺的價值在於把人力從繁瑣、重複性工作中解放出來,從而專注於問題本身。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在自建 APM 平臺中,要着重提高的是客戶端的抓取成功率和平臺易用性。前者是爲了儘量全面還原崩潰現場,後者是爲了儘可能提升團隊解決問題的效率。除了 APM 平臺,要長期解決崩潰問題還需要人和一套持續運轉的機制。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最初解決崩潰問題時,落腳點在於解決存量問題和遏制新增問題。首先,他們發起一場集中性戰役——全力解決存量崩潰問題。經過一個階段的治理,崩潰率下降到一個極低水平。其次,爲防止崩潰率再次飆升,他們又開始了崩潰預防,把問題消滅在萌芽階段:梳理出已解決的 Crash 問題,然後歸類,並由點到面地思考總結出預防手段。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"例如,針對服務端 API 髒數據引起的問題,統一在網絡層進行校驗和兜底;針對一些第三方庫引發的問題,用 AOP 手段進行 Hook。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在交付流程中,添加編譯檢查階段、自動化測試覆蓋、更廣的灰度觸達等環節,儘可能將問題提前暴露。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"即使問題發生在真實場景,APM 平臺也能實現分鐘級報警,並且自動把問題分配到相關同學。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"茄子科技前端負責人表示,“我們以 APM 平臺爲依託,預防爲主,建立了一套完善的響應機制”。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"App 優化實踐"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了應用崩潰,性能優化則是影響用戶體驗另一個關鍵。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"據瞭解,茄子科技旗下的 SHAREit,其用戶覆蓋 200 多個國家和地區。不僅用戶設備非常複雜,而且網絡狀況也不好,因此用戶更容易遇到 App 卡頓、啓動慢等體驗類問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲解決這些體驗類問題,他們首先搭建了基於線上的性能指標體系,充分衡量用戶遇到的體驗類問題。然後根據重要程度評估優先級,抓住重點,逐個擊破。比如,在啓動速度優化上,常規的優化手段作用有限,他們爲此開發和落地了任務調度框架。所有啓動任務通過任務調度框架進行調度,這樣不僅可以充分利用啓動階段 CPU 的能力,而且還能方便地收集到每一個任務在線上的真實執行情況。之後根據任務執行情況,進行鍼對性調整。同時,他們還通過 ASM 收集到在啓動階段搶先執行的 Msg,然後與業務方一起確認並且後移。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"網絡優化"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在網絡層面,針對海外用戶的網絡環境複雜、質量較低等問題,茄子科技通過HTTPDNS、協議優化、按需加載、CDN 預熱、離線資源包和服務端渲染等方案,大幅提升網絡請求的成功率。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"考慮到 SHAREit 也是一個內容型產品,他們還在播放器層面進行優化,加入多碼率預緩存策略、軟硬解碼自適應、動態追幀等技術,大幅提升視頻相關的業務指標。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"包體優化"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在包體積方面,茄子科技進行過一次大的優化,應用包大小從 42M 縮減爲15M。據悉,包體優化主要分爲兩個方面:技術層面和業務層面。在技術上,他們採取了五項舉措:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一是刪減無用代碼、資源,精簡第三方庫;二是代碼層面的統一優化,比如 R文件刪除、使用 R8;三是圖片壓縮、格式轉換和剔除重複圖片;四是資源的混淆;五是 dex 的再次壓縮。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在業務上,梳理出 App 的整體功能,下掉了滲透率較低的功能,並且對重點功能均實現 Bundle 化,這樣在減小包大小的同時,也方便在不同國家的精細化運營。並且,技術同學爲業務同學提供專門工具,從技術視角支撐和指導業務的優化方向。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"茄子科技前端負責人表示,體驗類的優化工作並不只涉及技術,單純的技術優化非常容易遇到瓶頸或遇到無法推動業務方修改的困境,從而無法實現優化收益最大化。因此,他們在 SHAREit 的實踐首先是調整項目定位,由技術同學主導,加入業務同學,統一戰線。在更高層面統一雙方目標,技術同學負責發掘優化項,提供工具和具體方案,業務同學負責更大規模的落地,各自發揮優勢,雙方共擔項目,共享成果。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了啓動、網絡優化、包體優化外,他們還對卡頓、內存、佈局、電量、存儲和線程等性能的多個維度進行優化,“我們的追求就是讓用戶在使用產品時有一個高品質體驗”。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"前端工程化建設:從石器時代到信息時代"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前端工程化,通俗的理解是,儘可能快速地實現可信賴的產品。其中兩個關鍵詞,“快速”,講求開發速度、構建速度、測試速度、問題定位速度;“可信賴”,包括代碼的質量、產品的性能、安全、及重現和定位問題能力。茄子科技前端負責人介紹,SHAREit 的前端工程化建設經歷了三個階段:石器時代,工業時代,信息時代。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"石器時代"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在石器時代,典型特徵是單工程的 MVC 架構,公司發展初期人比較少,反而效率更高,問題也比較少。但隨着公司發展越來越快,SHAREit 從單業務發展到多業務的平臺型產品時,單工程架構在人員節奏方面出現了較多問題,如代碼耦合導致一些問題浮現出來。這時,便開始工程組件化重構,進入到工業化時代。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"工業時代"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"工業化時代,典型的特徵是組件化。茄子科技使用的是多工程、多倉庫的方案,每個組件或 SDK 都有自己獨立的倉庫,都可以獨立於主工程進行單獨的編譯和運行。這方面分爲三大層:APP 的殼、業務層、基礎層,基礎層再往下還有更多、更細粒度的劃分。前端團隊自上而下通過 AAR 的方式進行依賴。組件化採用“分而治之”的思想,很好地解決了多團隊協同作戰的問題。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"信息時代"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"隨着公司孵化更多產品,每個產品在不同國家定製不同功能,規模越來越大了,用以前的組件化形式很難高效地支撐現有的業務,於是,茄子科技引入了GoogleBundle 技術,進入到信息化時代。其實針對這一問題,國內多采用插件化的方案,但由於海外不能做插件化技術,茄子科技只能運用谷歌自有特性。在信息化時代,典型的特徵是 Bundle 組件化。Bundle 模塊具有反向依賴的特徵。通過增加 Bundle 殼及自動化檢測工具的形式,讓 AppBundle 的特性和以前已有的組件化融合,讓開發者保持已有的開發模式。通過 AppBundle,可以做到針對每個國家用一個 APP 按需定製不同功能,如內容、直播、遊戲等。爲提升效率和質量,茄子科技還搭建了客戶端 CI\/CD 平臺,流程主要有編碼規範檢測、大文件 \/ 圖片檢測、靜態代碼掃描,關鍵文件觸發 Review、代碼Review、安全風險檢測,預編譯、包體積監控,編譯速度監控等,能做到自動打包、自動檢測、自動化測試與發佈。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"據悉,前端團隊已經涵蓋開發、測試、構建、部署一系列流程,通過自研APM 系統的預警機制、自動分配、輔助信息等,能夠及時發現且快速定位問題,並優化迭代,最終形成整個研發流程閉環。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Bundle 技術的應用與實踐"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2019 年,茄子科技前端團隊開始接觸 Bundle 技術,並在當時做了試點。通過這次嘗試,他們發現 Bundle 模塊的開發、編譯和發佈等都與原來的 Library形式有很多不同。因此,他們做了 Bundle 組件化。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"據悉,他們將 Bundle 的開發、編譯、發佈等特性與現有組件化方式融合,讓業務開發者保持原有開發模式對 Bundle 的特性幾乎無感知。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在具體實踐中,茄子科技前端團隊做了四件事:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第一,爲每個 Bundle 組件增加一個殼。Bundle 組件還是以原有的 Library 開發和發佈。Bundle 殼依賴 App 殼和 Bundle 組件。Bundle 殼中只是配置了Bundle 的集成屬性,無任何邏輯代碼。這樣很好的解決了 Bundle 模塊反向依賴宿主的特性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第二,資源隔離。Bundle 組件與宿主資源隔離,不能互相訪問。針對這種情況,他們做了自動化檢測工具,將問題暴露在編譯階段。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"舉個例子,GameListFragment 是 Bundle 組件的代碼,繼承了BaseRequestListFragment(宿主的基礎 SDK 代碼)。其佈局資源文件使用了 ActionPullToRefresh 控件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/af\/afdd58e89cb472a4ea09de1118254351.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當調用 findViewById(R.id.pull_to_refresh) 時,Bundle 組件調用了宿主資源,這樣編譯是可以通過的,但是會在運行時出現問題。解決辦法是由子類提供getXXId 方法,父類複寫即可。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/18\/1880df050d8b30455a924820e94269d5.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第三,APT\/AOP 等第三方庫不好用,他們通過修改其源碼進行適配。比如使用的 Android 路由框架 Router,修改其自定義的 GradlePlugin 的掃描Scope,讓它可以掃描到 Bundle 組件及其依賴。此外,通過 Bundle 模塊安裝狀態,判斷是否將 apt 生成路由信息注入到 ServiceLoaderInit,保證有無Bundle 模塊時,路由信息都可以成功註冊。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第四,線下模擬真實的 Bundle 模塊組合。結合 CI\/CD 在打包時,增加按國家、語言等多種維度的產出 aab 集合,方便測試人員在線下測試各種 Bundle 模塊組合成 aab 的情況。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"結果,Bundle 組件化的實施不僅極大減小了應用包大小,SHAREit 從原來的42M 縮小到 15M。而且,App 實現了動態按需下發,除本身支持的國家、語言、屏幕等之外,還會按照需求下發,比如創作者中心模塊只有在創作者登錄後纔會有入口引導下載。"}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章