背景
京東App在2019年做了一輪瘦身,當時(shí)做了全方位的瘦身,但隨著(zhù)業(yè)務(wù)快速迭代和新技術(shù)框架引入以及管控力度不足,安裝包體積出現了反彈。結合之前治而未管和業(yè)內一些同行的瘦身經(jīng)驗,本文介紹此次瘦身過(guò)程積累的一些實(shí)踐經(jīng)驗以及管控方案。首先拆分安裝包梳理數據,將安裝包的成份數據做成線(xiàn)上化看板,根據數據找出安裝包體積增長(cháng)主要因素,結合京東App現狀進(jìn)行了一系列綜合性的瘦身動(dòng)作,也沉淀了一套新的管控規范及管控平臺,最終實(shí)現了安裝包體積下降30%以上。
安裝包成份分析
瘦身未動(dòng),“數據”先行,包瘦身的第一步是要分析出App組成成份的大小數據,為包瘦身的目標設定及任務(wù)拆解提供數據支撐,達到可度量、線(xiàn)上化、統一數據標準的目的。如何進(jìn)行安卓App的包成份分析?安卓開(kāi)發(fā)者基本都會(huì )用到Android Studio自帶的Apk Analyzer工具,將Apk文件拖入Android Studio中即可查看,下圖是京東App拖入Android Studio中顯示的包大小數據,可以清晰的看出包體積里占比較大的主要是lib(存放動(dòng)態(tài)庫及安卓插件)、r(存放圖片、xml等資源)、assets目錄、dex和resources.arsc(資源映射表)幾類(lèi)文件。
apk Analyzer工具對于初步的、特定的包大小數據查看非常方便,但是對于需要進(jìn)行的包瘦身項目來(lái)看,數據過(guò)于籠統,沒(méi)有按模塊維度進(jìn)行劃分,無(wú)法將包瘦身責任劃分給對應的研發(fā)團隊,不便于后續包瘦身項目的具體實(shí)施。為了度量各模塊對包大小的影響,我們需要一套精細化的包大小分析方案。
首先來(lái)看一下京東App的工程結構,簡(jiǎn)化模型如下圖所示。京東App整體采用了插件化的架構,開(kāi)發(fā)迭代過(guò)程中逐漸按照功能、業(yè)務(wù)類(lèi)型拆分成了很多模塊,各模塊有獨立的項目工程、倉庫,日常進(jìn)行獨立的開(kāi)發(fā)維護,最終集成在A(yíng)pplication工程中。功能型的模塊基本上是以library依賴(lài)的形式引入到工程中的,比如常用的網(wǎng)絡(luò )庫、圖片庫、埋點(diǎn)庫等;業(yè)務(wù)型的模塊大多是以插件的形式存在,比如搜索、商詳、購物車(chē)等;此外主工程中還存在少部分未拆分成模塊的歷史遺留邏輯,在日常迭代中占比非常少。
基于京東App模塊化的現狀,安裝包的成份分析可以從三個(gè)方面進(jìn)行:分析包成份的總覽數據、分析插件數據和分析library數據。
01
Apk總覽數據分析
安卓的包產(chǎn)物apk文件本質(zhì)上是一個(gè)zip文件,可以用zipinfo命令輸出壓縮包中每個(gè)文件的詳細信息日志,用法:
Zipinfo -l --t --h xxxx.apk > xxxx.txt
輸出的日志文件打開(kāi)如下圖,每個(gè)文件的壓縮信息一行,包括文件名、原始大小、壓縮后大小等指標。
對以上日志信息進(jìn)行逐行解析,根據解混淆后的文件名路徑、文件類(lèi)型進(jìn)行歸類(lèi)統計,即可得出apk的總覽信息,包括各類(lèi)型文件的數量、總大小、單一文件大小等指標,并建立文件大小索引。
02
插件數據分析
插件產(chǎn)物的本質(zhì)也是apk,目前京東App的預裝插件有50+,App打包時(shí)各插件會(huì )以.so為后綴的形式放在lib目錄中。對插件的分析可以直接基于包產(chǎn)物進(jìn)行,解析時(shí)只需要將apk解壓開(kāi)來(lái),按照文件名特征將插件從全部so文件中分離出來(lái),逐一按照上述zipinfo相同方法進(jìn)行解析即可。
03
library數據分析
各library模塊大多是以aar/jar依賴(lài)的形式引入到項目工程中,目前京東App的全部項目依賴(lài)庫有300+,包括直接依賴(lài)、間接依賴(lài)。對library數據的分析主要是在分析各依賴(lài)對包大小的影響和分析維護開(kāi)發(fā)者兩個(gè)方面。
01
分析依賴(lài)對包大小影響
安卓工程在構建時(shí),Gradle構建工具會(huì )自動(dòng)將依賴(lài)庫同步緩存到本地,與工程中的代碼資源一起編譯merge最終輸出apk。通過(guò)編寫(xiě)gradle腳本任務(wù)在構建過(guò)程中可以取到aar/jar依賴(lài)文件,但是考慮到分析工具的獨立性,盡量避免和打包流程耦合在一起,采用了自行解析依賴(lài)的方式來(lái)實(shí)現,整體流程如下:
a. 打包過(guò)程中執行g(shù)radle命令生成app運行時(shí)依賴(lài)樹(shù)日志文件,用法:
./gradlew:app:dependencies--configuration xxxReleaseRuntimeClasspath > dep.txt
生成的日志文件部分如下:
b.解析日志文件生成N叉樹(shù)模型,每一行日志生成一個(gè)依賴(lài)節點(diǎn),包含groupId、artifact_name、version信息,日志中的->表示版本沖突取高版本,(*)表示重復依賴(lài),在application和common_library中添加的依賴(lài)為直接依賴(lài),對應根節點(diǎn)和common_library節點(diǎn)的子節點(diǎn),其它節點(diǎn)為間接依賴(lài)。
c.將N叉樹(shù)扁平化去重逐一解析依賴(lài),下載aar/jar文件。解析過(guò)程中拼接url輪詢(xún)倉庫,為了加快解析速度,可以根據依賴(lài)的groupId區分依賴(lài)的來(lái)源,如果是jd內部的依賴(lài),則優(yōu)先請求內部的私服,否則優(yōu)先訪(fǎng)問(wèn)常用的鏡像倉庫。此外可以加入LRU緩存機制,本地存在的依賴(lài)產(chǎn)物不再請求倉庫,進(jìn)一步加快解析速度。存在的一個(gè)潛在問(wèn)題是快照版本的依賴(lài)會(huì )對應多個(gè)產(chǎn)物,解析時(shí)取的最新版本可能和app構建時(shí)用的不是同一個(gè),通常發(fā)版分支上的依賴(lài)被規定不能用快照版本,所以這個(gè)問(wèn)題也就間接規避了。
d. aar也是一種zip文件,同樣可以用zipinfo命令來(lái)解析組成成份大小,建立內部jar、so、圖片、xml、asset等文件的索引。
e.將aar內部的各文件根據名稱(chēng)與總覽數據分析時(shí)建立的文件大小索引進(jìn)行關(guān)聯(lián),即可知道aar內部的so、圖片、xml、asset等文件最終在apk壓縮包里的大小。由于代碼文件jar最終經(jīng)過(guò)編譯、刪減、混淆merge成了dex,無(wú)法精確的溯源,代碼部分的大小計算采用了原始大小按比例折算的方式進(jìn)行。折算比例是實(shí)驗去掉比較獨立的依賴(lài)后打包看包大小的差值,去除非代碼部分的大小后,即可算出代碼部分的折算比例,重復N次去除不同的依賴(lài)取平均值。
02
分析模塊負責人
前述京東App有300+依賴(lài),由于業(yè)務(wù)調整、人員變動(dòng)等原因沒(méi)有一個(gè)統一的library維護關(guān)系數據。對于直接依賴(lài)部分,初期通過(guò)git blame 命令輸出app、common_library工程的build.gradle文件修改記錄日志,正則匹配出依賴(lài)和維護者的對應關(guān)系;對于間接依賴(lài)部分,將前述N叉樹(shù)的每一個(gè)間接依賴(lài)節點(diǎn),向上尋找它的父節點(diǎn)直到直接依賴(lài)節點(diǎn),則直接依賴(lài)的開(kāi)發(fā)者也是此間接依賴(lài)引入的責任人,會(huì )存在多個(gè)直接依賴(lài)引用了同一個(gè)間接依賴(lài)的情形,即某個(gè)間接依賴(lài)庫對應多個(gè)引入責任人。
通過(guò)上述分析方案,已經(jīng)可以完整的分析出安裝包的各成份大小及對應的負責人,為瘦身工作的實(shí)施提供了數據指引。分析方案全部是用python腳本的方式實(shí)現,已集成到公司內部的CD系統,數據看板效果如下。
京東App的開(kāi)發(fā)者團隊規模較大,在安裝包成份數據線(xiàn)上化建設完成后,所有開(kāi)發(fā)者統一了看數標準和渠道,每個(gè)App版本的增長(cháng)與下降也可以歸因到具體模塊級別以及對應的開(kāi)發(fā)者,同時(shí)這個(gè)數據分析看板也為瘦身的度量和管控提供了基礎數據能力。根據數據分析我們發(fā)現App內資源文件、ReactNative、插件的數量和體積都比較大,所以瘦身重點(diǎn)圍繞這幾方面進(jìn)行專(zhuān)項治理,同時(shí)基于之前瘦身出現過(guò)反彈的教訓,分析發(fā)現在過(guò)往開(kāi)發(fā)迭代中缺乏約束管控,技術(shù)選型和研發(fā)復用也暴露了部分問(wèn)題,因此制定了精細化的管控規范并在研測流程中落地實(shí)施。
瘦身方案
借鑒業(yè)內的瘦身措施,結合京東App自身特性制定了瘦身方案,首先進(jìn)行了常規化瘦身舉措主要包括壓縮資源、內置ReactNative轉為下載、插件轉下載、R8編譯升級等,其次對App內的圖片進(jìn)行專(zhuān)項治理,構建了圖片管理平臺,最終使安卓安裝包體積下降了近30%+,包體積瘦身趨勢圖如下圖。
01
瘦身措施
- 插件轉下載
京東mPaaS平臺提供了安卓端插件轉下載安裝的能力,2022年初我們協(xié)同mPaaS團隊、運維團隊共同對其進(jìn)行優(yōu)化提升了其可用性和穩定性。將下載器的unknown host問(wèn)題解決,加入了httpdns能力,支持切換域名能力等,運維團隊通過(guò)撥測解決某些cdn節點(diǎn)異常導致的文件md5不一致問(wèn)題,最終在真實(shí)用戶(hù)網(wǎng)絡(luò )環(huán)境下平均單次下載網(wǎng)絡(luò )請求成功率在98%以上,由于在用戶(hù)進(jìn)入頁(yè)面前存在多次觸發(fā)下載的機會(huì ),進(jìn)入頁(yè)面而未加載成功場(chǎng)景的概率在十萬(wàn)分之4以下,此概率已低于A(yíng)pp的崩潰率,體驗上已完全可以保障,實(shí)踐過(guò)程中也未出現用戶(hù)體驗異常反饋。通過(guò)業(yè)務(wù)模塊的uv進(jìn)行倒排序,最終甄選了10+插件進(jìn)行了轉下載安裝。
- RN內置包轉下載
在優(yōu)化之初,京東大部分用React Native開(kāi)發(fā)的業(yè)務(wù)模塊均采用內置App的方式,峰值數量達到50+個(gè)。聯(lián)合mPaaS團隊進(jìn)行優(yōu)化,最初評估采用Brotli壓縮本地內置包,經(jīng)評估其解壓耗時(shí)是gzip的2倍,內存使用是幾十至幾百倍,最終放棄這個(gè)方案。統一采用了將內置包轉下載安裝的方案,通過(guò)拆分基礎包和業(yè)務(wù)包將JSBundle體積縮小,設置了預下載非強制更新、預下載強制更新以及增加某個(gè)RN業(yè)務(wù)打開(kāi)則觸發(fā)其他未下載業(yè)務(wù)下載等策略有效提升了單周新版更新覆蓋率,提供h5降級、統一兜底降級等多重兜底體驗,最終提升了RN相關(guān)能力的穩定性和可用性,保障了用戶(hù)體驗,目前內置的RN業(yè)務(wù)模塊數已經(jīng)降至個(gè)位數。
- 資源壓縮
針對圖片壓縮,借助集成TinyPNG壓縮工具對10K以上的圖片進(jìn)行壓縮;受限于A(yíng)ndroid工程minSdkVersion=16,不能將所有的png圖片都轉成webp格式,只能將無(wú)透明通道的圖片轉成webp。后期對包大小更嚴格的要求,可以考慮將圖片遠程化。
- iconfont
借助瘦身腳本分析工具掃描安裝包中方正純色的小圖片轉成iconfont,包內掃描出800多張可轉成iconfont,數量較多需要長(cháng)期治理,雖然收益不明顯但利于各個(gè)業(yè)務(wù)零散的小圖標進(jìn)行統一管理,同時(shí)有利于A(yíng)pp整體視覺(jué)風(fēng)格的統一。
- R文件內聯(lián)
通過(guò)自定義Gradle插件利用ASM對class文件進(jìn)行字節碼操作,將class文件中引用到R類(lèi)資源id替換成對應的常量值并刪除R文件,達到減少包體積的目的。由于京東App采用插件化的方式開(kāi)發(fā)業(yè)務(wù),每個(gè)業(yè)務(wù)插件都會(huì )引用公共資源,不能簡(jiǎn)單的將公共資源id添加到白名單中,所以結合京東App插件化方案(aura)的公共資源id固定的能力,實(shí)現插件化工程中的公共資源內聯(lián)。目前開(kāi)啟R文件內聯(lián)已完成灰度,后續會(huì )陸續上線(xiàn),包體積整體收益5.5MB以上。
- 開(kāi)啟R8編譯
隨Target31適配升級AGP到4.1版本,同步開(kāi)啟了R8編譯,需要注意R8會(huì )忽略部分Proguard混淆規則,針對R8編譯對混淆規則的變更點(diǎn),編寫(xiě)了mapping文件檢測腳本工具,對Proguard和R8編譯生成的mapping混淆文件進(jìn)行對比,檢測網(wǎng)絡(luò )解析、反射相關(guān)的類(lèi)是否添加了正確的混淆規則,利用工具保障了升級R8編譯后App的穩定性。R8編譯對代碼混淆優(yōu)化效果要優(yōu)于Proguard,開(kāi)啟R8后,包體積減少1.6MB,構建耗時(shí)也降低30%(3min)以上。
- 7Zip壓縮
7Zip兼容Zip格式,支持Zip的Deflate算法,使用7zip極限壓縮模式比Android打包默認的zip壓縮率高,可以將加固好的apk文件進(jìn)行7zip解壓縮后重新簽名,包體積降低約2.2%(2MB)。為了驗證其穩定性與可用性,計劃對內部和外部市場(chǎng)都分別進(jìn)行灰度,通過(guò)京東內部灰度渠道已灰度完成,對外部的各個(gè)渠道將包直接投遞到渠道,如小米、華為等在灰度進(jìn)行中。
在完成這些常規瘦身方案優(yōu)化后,App安裝包大小依舊較大。先前圖片資源優(yōu)化主要是通過(guò)圖片壓縮、使用一套分辨率xhdpi圖片、圖片轉webp、資源名混淆等方式進(jìn)行,這些優(yōu)化方式都是將內置的圖片進(jìn)行瘦身優(yōu)化,隨著(zhù)業(yè)務(wù)功能的不斷迭代,瘦身收益將越來(lái)越少。京東App各個(gè)業(yè)務(wù)的開(kāi)發(fā)方式是以插件化的形式進(jìn)行,通過(guò)對這些業(yè)務(wù)插件包的分析,發(fā)現這些業(yè)務(wù)模塊中內置的圖片資源大小占各自插件包總大小的25%以上,針對內置圖片資源過(guò)多問(wèn)題,提出一種圖片資源遠程化的優(yōu)化方案。
02
圖片資源遠程化
直接將圖片資源轉成在線(xiàn)加載可能會(huì )影響用戶(hù)體驗,將內置的圖片資源從App應用內剝離,然后將圖片上傳到CDN獲取到圖片的網(wǎng)絡(luò )鏈接,應用通過(guò)加載圖片鏈接進(jìn)行在線(xiàn)加載的方式來(lái)展示圖片,在線(xiàn)加載圖片需要經(jīng)過(guò)下載圖片的過(guò)程,考慮到用戶(hù)所處的網(wǎng)絡(luò )環(huán)境受多種因素的影響,常使用默認的兜底圖在圖片展示區域進(jìn)行占位,或者使用全屏的加載中動(dòng)畫(huà)進(jìn)行過(guò)渡等方式來(lái)優(yōu)化用戶(hù)體驗。從優(yōu)化用戶(hù)體驗角度出發(fā),提出一種2層網(wǎng)絡(luò )加載+3層降級措施的優(yōu)化方案,可以提高網(wǎng)絡(luò )圖片的加載成功率,同時(shí)給用戶(hù)展示圖片的體驗如同加載應用內置圖片資源一樣,整體方案如下:
該優(yōu)化方案主要由圖片管理CMS平臺和獲取圖片信息的客戶(hù)端組件組成,由業(yè)務(wù)研發(fā)在CMS配置各自模塊的圖片信息,客戶(hù)端通過(guò)組件獲取圖片地址展示圖片。
01
CMS圖片管理
- CMS支持圖片類(lèi)型包括png、jgp、webp、點(diǎn)9圖等,由業(yè)務(wù)模塊梳理出內置的圖片素材;
- CMS以用戶(hù)為單位,創(chuàng )建業(yè)務(wù)模塊,在模塊內上傳圖片獲取圖片CDN鏈接,提供2x、3x兩個(gè)分辨率設置,同時(shí)可以設置圖片在客戶(hù)端生效的版本范圍以及生效平臺Android或iOS;
- CMS提供將圖片打包成zip壓縮包的功能,在用戶(hù)上傳完圖片后,會(huì )根據各個(gè)圖片生效的客戶(hù)端版本區間,生成不同版本區間以及2x、3x兩種分辨率的zip包,會(huì )根據客戶(hù)端請求接口中的分辨率參數下發(fā)不同分辨率的zip包鏈接;
- CMS提供zip包的預加載策略,分為App啟動(dòng)時(shí)預加載、App首頁(yè)加載成功后x秒開(kāi)始預加載、進(jìn)入某頁(yè)面時(shí)預加載等;
- 用戶(hù)根據前面步驟配置完成后,將業(yè)務(wù)模塊內的圖片名和對應的CDN鏈接組成配置信息由CMS導出,然后將該配置信息文件內置到客戶(hù)端,提供跟隨安裝包的兜底體驗。
02
客戶(hù)端圖片預加載與緩存
- 客戶(hù)端組件在A(yíng)pp啟動(dòng)時(shí)請求CMS后臺提供的查詢(xún)接口,獲取當前客戶(hù)端版本對應的配置信息,與本地兜底配置信息進(jìn)行比較,得到的差分信息進(jìn)行本地持久化;
- 客戶(hù)端組件同時(shí)解析后臺接口下發(fā)的圖片zip包信息,根據zip包的預加載策略下載對應的圖片zip包,下載完成后解壓到以模塊名命名的本地文件夾中;
- 當展示圖片時(shí),客戶(hù)端組件根據模塊ID+圖片ID優(yōu)先查詢(xún)本地zip包解壓目錄中該圖片是否已存在,如果該圖片已下載,則直接返回該圖片的本地路徑path,客戶(hù)端直接使用該本地路徑path展示圖片;如果該圖片還未下載完成,或不存在,則從內置的配置信息文件中獲取該圖片的CDN鏈接,同之前一樣加載圖片的網(wǎng)絡(luò )鏈接;如果加載該網(wǎng)絡(luò )鏈接還是失敗,則展示默認的兜底圖。
該優(yōu)化方案中,2層網(wǎng)絡(luò )加載的第一層網(wǎng)絡(luò )加載是指業(yè)務(wù)模塊的zip包,下載一次多版本復用;第二層網(wǎng)絡(luò )加載是指圖片zip包預加載未完成或失敗時(shí),通過(guò)安裝包中內置的圖片CDN鏈接兜底配置信息加載圖片網(wǎng)絡(luò )鏈接來(lái)展示圖片,該兜底配置信息以圖片名+圖片CDN鏈接鍵值對的形式保存。
3層降級措施的第一層降級是獲取圖片zip包預加載的本地緩存圖片;第二層降級是當本地圖片緩存失效時(shí),獲取內置的圖片CDN鏈接加載;第三層降級則是默認的兜底圖。瘦身業(yè)務(wù)模塊開(kāi)啟圖片zip預加載功能時(shí),CMS支持業(yè)務(wù)模塊設置zip包預加載策略,業(yè)務(wù)模塊根據實(shí)際情況選擇不同的加載策略,例如某些業(yè)務(wù)模塊頁(yè)面入口比較深,這些業(yè)務(wù)模塊就可以選擇用戶(hù)在進(jìn)入頁(yè)面時(shí)觸發(fā)zip包的預加載;
某些業(yè)務(wù)模塊進(jìn)入頁(yè)面入口比較淺就可以設置App啟動(dòng)后空閑時(shí)預加載,其他二級、三級的頁(yè)面可以設置App啟動(dòng)后x秒(隨機時(shí)間)才開(kāi)始下載。通過(guò)設置這些預加載策略,將圖片zip包分散在不同時(shí)間段下載,降低圖片加載時(shí)的網(wǎng)絡(luò )流量峰值。
上述方案需要業(yè)務(wù)研發(fā)積極配合,需要業(yè)務(wù)模塊額外通過(guò)固定的模塊ID+圖片ID優(yōu)先獲取本地緩存的方式加載圖片,使用過(guò)程較繁瑣,缺乏靈活性,為方便開(kāi)發(fā)者更容易使用,提供更多選擇,降低接入成本,我們在CMS和客戶(hù)端作了些改進(jìn),又提出了下面的優(yōu)化方案:
- 圖片CMS變更點(diǎn)
CMS后臺對圖片zip打包做額外處理:取圖片CDN鏈接中固定路徑計算MD5值,并將該MD5值重命名圖片名稱(chēng),不保留圖片類(lèi)型后綴。例如鏈接:https://xxx/jfs/xxx/name.png,對鏈接中jfs開(kāi)頭和.png之間的路徑字符串進(jìn)行MD5值計算:MD5(/jfs/xxx/name) =MD5-Value ,以該MD5-Value值作為圖片名稱(chēng),并且移除圖片類(lèi)型后綴名(.png等), 其他圖片同樣按照該步驟進(jìn)行重命名,最后打包成以模塊名命名的圖片壓縮包。
- 客戶(hù)端變更點(diǎn)
提供一個(gè)工具類(lèi),以模塊名和圖片CDN鏈接作為入參,對圖片CDN鏈接進(jìn)行相同的處理,獲取CDN鏈接中固定路徑字符串的MD5值,然后根據模塊名和MD5值查找該圖片本地緩存是否已存在,即圖片zip包預加載是否成功,如果圖片已成功緩存則將該圖片的緩存路徑(file://協(xié)議)返回給圖片加載框架,如果圖片緩存不存在,則原樣返回圖片CDN鏈接(http://協(xié)議)。同樣也可以結合圖片加載框架實(shí)現,新增以業(yè)務(wù)模塊名作為別名的圖片加載屬性,該別名屬性就是業(yè)務(wù)模塊圖片緩存本地的目錄,如果設置了該屬性,圖片加載框架就會(huì )優(yōu)先判斷該圖片是否已緩存成功,若圖片已緩存成功,則通過(guò)本地路徑展示圖片,若緩存未成功,則直接在線(xiàn)加載圖片,該方案需要客戶(hù)端圖片加載框架同時(shí)支持file協(xié)議和http協(xié)議。該方案也可以應用于大促等場(chǎng)景的圖片預加載。
通過(guò)埋點(diǎn)數據查看zip包預加載成功率,圖片zip包預加載在配置首頁(yè)啟動(dòng)后x秒下載,iOS端的zip包加載成功率在98.9%~99.1%波動(dòng),Android端的zip加載成功率在96.7%~98.1%波動(dòng),剩余1%~3%的用戶(hù)則通過(guò)內置的兜底cdn鏈接在線(xiàn)加載,倆者結合展示圖片的成功率接近100%;客戶(hù)端從本地緩存加載圖片的效率要高于從網(wǎng)絡(luò )在線(xiàn)加載圖片,省去了等待圖片下載的過(guò)程,降低了默認兜底圖的展示概率,在體驗上接近于本地內置圖片加載的效果。
管控
京東App在2019年專(zhuān)項瘦身后經(jīng)過(guò)一段時(shí)間又反彈了50%,一方面因為業(yè)務(wù)快速迭代發(fā)展以及新引入了一些比較大的基礎庫,另一方面原因就是重點(diǎn)做了瘦身治理卻沒(méi)有深入做管控準入防止劣化。因此在新一輪瘦身優(yōu)化過(guò)程中,我們一邊做瘦身治理,一邊在探索常態(tài)化的管控機制,最終沉淀了一套管控規范和管控平臺。
管控的目的不是為了限制需求迭代與增加代碼,目的是做好把控讓合理的代碼放進(jìn)來(lái),不合理的拒絕掉以及淘汰陳舊的代碼,提升開(kāi)發(fā)者們的瘦身意識。管控對產(chǎn)研全鏈路提出了新的要求,對于以往粗放型的需求開(kāi)發(fā)迭代方式會(huì )有所約束,產(chǎn)品側需要及時(shí)的配合研發(fā)側將ABTest的廢棄實(shí)驗代碼下線(xiàn),研發(fā)側需要做好技術(shù)規劃和技術(shù)選型,盡可能的精簡(jiǎn)代碼和復用代碼,加強技術(shù)團隊之間的協(xié)同,不放置過(guò)大或冗余的資源文件等。
管控的前提是基于A(yíng)pp已經(jīng)充分的組件化解耦,且App開(kāi)發(fā)人員規模相對比較大,比如京東App當前安卓端是由業(yè)務(wù)插件和AAR依賴(lài)庫組成。App的組成成份整體上是由一個(gè)配置表來(lái)表示,配置表內描述所有組件的配置版本信息,一個(gè)組件是否允許集成到App內的前提是這個(gè)組件體積增長(cháng)大小符合我們制定的規范,然后組件的最新版本才可以集成到配置表內。我們管控的核心機制就是控制配置表的更新集成權限,以此來(lái)約束每個(gè)組件的開(kāi)發(fā)者,通過(guò)App架構師委員會(huì )(京東內由架構師形成的虛擬組織)制定的公共管控規范來(lái)評判每個(gè)組件體積增長(cháng)是否合規,合規則準入更新配置表,否則走異常申請流程。
01
管控流程與方案
管控流程是伴隨著(zhù)開(kāi)發(fā)、測試、發(fā)布整個(gè)流程進(jìn)行的,整體的流程如圖所示。開(kāi)發(fā)者開(kāi)發(fā)完成后,由開(kāi)發(fā)者在京東內部的組件構建與發(fā)布平臺(mPaaS)發(fā)布形成新的組件版本,在構建完成后開(kāi)發(fā)者在打包平臺(Bamboo)基于臨時(shí)配置表進(jìn)行打包并提測,在此過(guò)程中會(huì )基于包計算出對應組件的體積變化數據,同時(shí)打包系統會(huì )以郵件形式將數據同步至開(kāi)發(fā)者,盡可能前置的告知開(kāi)發(fā)者是否存在不合規;
測試通過(guò)后開(kāi)發(fā)者申請集成組件版本信息至配置表,若合規則更新配置表且流程結束,若不合規開(kāi)發(fā)者可以進(jìn)行代碼優(yōu)化后在申請集成,或者申請異常審批流程至App架構師委員會(huì )。常規的業(yè)務(wù)迭代大部分不會(huì )出現違規,但也會(huì )存在部分異常的情況,異常的處理往往會(huì )產(chǎn)生較多的爭議,研發(fā)的安裝包體積管控需要頂住產(chǎn)品和業(yè)務(wù)的壓力,異常處理的流程需要客觀(guān)、透明、靈活,異常處理流程如圖所示。
某個(gè)組件違規了發(fā)起申請,對于三方庫、國家要求的隱私整改、安卓系統升級適配等情況,可以審核準許通過(guò),其他一些特殊情況也可以通過(guò),由App架構師委員會(huì )來(lái)酌情把控。對于一些正常情況下產(chǎn)生了組件體積增長(cháng)超限違規,短期無(wú)法優(yōu)化瘦回可以申請設置未來(lái)n個(gè)版本后瘦回,或者A組件協(xié)商B、C組件來(lái)協(xié)助分擔瘦回,最終瘦身方案由App架構師委員會(huì )判斷合理性,后續在n個(gè)版本內由平臺自動(dòng)計算瘦身的階段性進(jìn)展數據告知瘦身發(fā)起的相關(guān)人員;在n個(gè)版本后若未達成瘦身既定計劃的目標則進(jìn)行線(xiàn)下專(zhuān)項討論,重新制定計劃或做一些公示性處罰。
判斷一個(gè)組件合規與否依賴(lài)于管控規范,管控規范由App架構師委員會(huì )共同討論制定,最初我們設定的管控規范比較粗糙:每個(gè)組件當前版本相對于上個(gè)版本應小于等于n KB(起初n設置為100),隨著(zhù)瘦身進(jìn)展到后期發(fā)現其存在比較多的弊端,對于體積大的模塊其往往是多個(gè)開(kāi)發(fā)者協(xié)作經(jīng)常容易出現違規,對于體積小的模塊往往參與人少迭代變化也不頻繁,所以規范根本對其不起作用。
基于這個(gè)情況我們改進(jìn)了規范:將組件分為A1和A2兩類(lèi),A1類(lèi)組件>=n MB,A2類(lèi)組件<n MB,對于A(yíng)1類(lèi)組件限定其年增幅應小于等于x%,對于A(yíng)2類(lèi)組件限定其單個(gè)版本增量應<=yKB。A1類(lèi)模塊一般迭代頻率高且開(kāi)發(fā)人員多,限定年增幅度每個(gè)版本的回旋調整余地更大;對于A(yíng)2類(lèi)模塊迭代頻率一般不高,設定單個(gè)版本增量更小也可以對應的約束其增長(cháng);對于一些三方庫、公共庫設置白名單處理;從全局來(lái)看管控規范基本兼具一定的公平性和合理性。結合App實(shí)際情況,通過(guò)合理的設定n、x及y值,可以將增幅控制在計劃范圍內。
總結
隨著(zhù)業(yè)務(wù)和用戶(hù)需求的發(fā)展,App的體積會(huì )保持向上增長(cháng)的趨勢,業(yè)內各個(gè)體積較大的App都在進(jìn)行著(zhù)瘦身。我們借鑒了業(yè)內一些優(yōu)秀的瘦身方案,結合京東App的實(shí)際場(chǎng)景進(jìn)行了多項措施相結合的綜合性瘦身方案,提出了既要治理又要管控的思路,沉淀了一套管控規范,并將其落地到研發(fā)測試流程系統內。隨著(zhù)新技術(shù)的迭代更新,新的瘦身方案與措施會(huì )不斷涌現,我們會(huì )持續保持跟蹤,歡迎讀者們交流拍磚。
【參考資料】
1、5年磨一劍|優(yōu)酷Android包瘦身治理思路全解:https://developer.aliyun.com/article/953463
2、抖音包大小優(yōu)化-資源優(yōu)化:https://juejin.cn/post/6844904106696376334
3、Google縮減應用大?。?/span>https://developer.android.google.cn/studio/build/shrink-code#shrink-resources
4、Android App包瘦身優(yōu)化實(shí)踐:https://tech.meituan.com/2017/04/07/android-shrink-overall-solution.html
5、百度App Android包體積優(yōu)化實(shí)踐:https://mp.weixin.qq.com/s?__biz=MzUxMzk2ODI1NQ==&mid=2247485123&idx=1&sn=09e7fb181f8d1e8d0736931c42be0707&chksm=f94c57d3ce3bdec52c78db8b86e1fdac1f4564ccff94382aec1e5fb060b543ab134de7ac9c3d&scene=21#wechat_redirect
6、R8:https://r8.googlesource.com/r8
7、京東插件化工程R文件瘦身技術(shù)方案:https://mp.weixin.qq.com/s/JgNugWIyor23J-nrKMC6dg
8、京東到家Android瘦身探索:https://mp.weixin.qq.com/s/qqa931j9Jw2ojCX18E1–g
作者:App瘦身團隊
來(lái)源:微信公眾號:京東零售技術(shù)
出處:https://mp.weixin.qq.com/s/sSpbbq-v04CAB5ifKmC20g
如若轉載,請注明出處:http://www.asergeich.com/20493.html