作者:閑魚技術-金喏
1.背景
不知大家是否注意到,閑魚的包大小在随着服務使用者的增多和新業務的持續疊代不斷增長。在過去的半年時間,Android端包大小漲了43%,iOS端也漲了26%,若再不加管控,按照目前的增長速度再過1年可直逼200M。
包大小對應用的下載下傳轉化率和留存率起到至關重要的作用,瘦包作為閑魚技術今年的重點項目,意義非同凡響。
對于閑魚來說,瘦包能提升應用的下載下傳轉化率。首先是蘋果公司不支援流量下載下傳超過200M的包,包越大下載下傳時間越長失敗率越高,且據Google play統計,包大小每增加6M,下載下傳轉化率會下跌1%。此外也能提升使用者的留存率,因為當使用者手機記憶體不夠用時,肯定是優先删除占記憶體比較大的app。
對開發來說,定期清理廢棄代碼、不用的SDK和整理重複的資源,有助于提高代碼的健康度和架構的合理性。
但随着每一次用戶端釋出,業務導緻的增量使包大小默默攀升。業務需求是要做的,包大小也是要控的,為了讓每一次新需求內建時,開發們能切實感受到對整體包大小的影響,讓每一次包大小變動都有迹可循,需要對包大小進行科學分析。
現在開發每次送出代碼後,會觸發打包平台自動打包,包建構成功後會自動進行包對比分析,并發回開發包增量分析報告郵件。在介紹這個包增量分析工具之前,先給大家科普下安裝包的構成。
2.安裝包構成
2.1 Android包構成
Android安裝包是以字尾名為“apk”的壓縮包。閑魚安裝包解壓後的主要構成如下:
AndroidManifest.xml
配置檔案:包含包名、元件、權限的配置資訊等。
resources.arsc
android資源索引表:包含資源名稱、類型、值、ID和配置資訊。
classes.dex
:是很多.class檔案處理後的産物,最終可以在 Android 運作時環境執行。
/assets
:放置随包打入的檔案,如text、二進制資料等。
/res
資源檔案:存放圖檔、字元串、布局檔案、xml檔案等,運作打包時沒有使用的檔案資源不會打入包中。
/lib
:程式運作時依賴的庫。
2.2 iOS包構成
iOS安裝包是字尾為“ipa”的壓縮包,解壓後主要包含以下幾部分:
簽名檔案:裡面的CodeResources包含對bundle中的所有資源檔案的簽名資訊。
資源檔案:程式運作過程中需要的資源,比如圖檔、音頻、視訊、nib檔案、配置檔案等。
可執行檔案:是通過編譯器、連接配接器将我們編寫的代碼、靜态庫、動态庫編譯成的檔案,是程式的主體。其中靜态庫在連結時會被完整的複制到可執行檔案中,被多次使用就有多份拷貝,動态庫則是連結時不複制,程式運作時由系統動态加載到記憶體,系統隻加載一次,多個程式共用。
bundle檔案:工程中使用的其他第三方或資源的bundle。
3.包分析方案
3.1 Android包分析
apk壓縮包按照資源檔案類型分類,主要有:so資源(程式運作依賴的庫,如接入UC浏覽器核心SDK時,引入的so達到驚人的12M)、圖檔資源(png、webp、jpg等)、Java代碼(dex檔案)、xml代碼這幾類,此外還可橫向統計flutter相關資源情況。
由于可以拿到單個檔案的資訊,是以我們開發了工具解析apk包中的内容,從檔案類型角度分析包資源占比情況,以及将資源檔案按照大小排序展示,并以圖表形式直覺告訴開發資源情況。
3.2 iOS包分析
我們現在是從靜态庫、動态庫、資源次元來分析包内容和增量。
3.2.1 靜态庫大小
通過分析link map檔案獲得靜态庫大小,網上有很多link map的解析工具。核心是解析link map檔案中的Object files和Symbols中的資料。
#Object files:
[ 0] linker synthesized
[ 1] /Users/xy/build/workspace/Pods/MNN/MNN.framework/MNN(MNNReluInt8-24695ca527c0186b07.o)
[ 2] /Users/xy/build/workspace/Pods/MNN/MNN.framework/MNN(MNNGemmInt16to32_4x4_Unit-416f183fdf349b160b82.o)
[ 3] /Users/xy/build/workspace/Pods/MNN/MNN.framework/MNN(CPUPermute.o)
每一行代表對應可執行檔案的編号,如CPUPermute.o檔案編号是3。
#Symbols:
#Address Size File Name
0x100006CA0 0x000000EC [ 3] __MNN10CPUPermuteC2EPNS_7BackendEPKNS_2OpE
0x100006D8C 0x0000004C [ 3] __MNN2Op15main_as_PermuteEv
0x100006DD8 0x00000008 [ 3] __MNN10CPUPermute8onResizeERKNSt3_$body
0x100006DE0 0x00000580 [ 3] __MNN10CPUPermute9onExecuteERKNSt3_$body
0x100007360 0x00000034 [ 3] __MNN38___CPUPermuteCreator__OpType_Permute__Ev
0x100007394 0x00000070 [ 3] __MNN10CPUPermuteD1Ev
在Symbols部分,Address是偏移位址,Size是所占記憶體大小,File所指的編号就是Object files中的檔案編号,将Symbols中所有相同編号的Size累加起來,即可獲得該編号對應可執行檔案的大小了。
然後根據可執行檔案的目錄資訊,可知該檔案所屬的靜态庫,進而計算出對應靜态庫代碼大小。此外靜态庫大小在計算時需要排除對應的
dead Stripped Symbols
的大小來獲得真正的二進制大小,因為這些是無用的符号,連結的時候不會加入。
3.2.2 動态庫大小
動态庫通常位于安裝檔案根目錄下的Frameworks目錄中,通常是一個字尾為.framework的檔案夾。
由于動态庫在arm64架構下比較大,且蘋果在拆包階段會将打包好的通用的主二進制和動态庫拆分成單架構的主二進制和動态庫,是以隻要是涉及二進制隻計算arm64單架構的情況即可。
動态庫大小是直接用
lipo
拆成arm64單架構大小後計算framework檔案夾占磁盤大小獲得。
3.2.3 子產品大小
子產品大小為庫大小+該子產品所有資源檔案占磁盤的大小,若為動态庫的子產品,則資源大小僅計算拷貝到主bundle的資源。分析結果如下圖所示:
3.需求增量卡口
瘦包主要靠開發努力,要讓大家在平時開發中有瘦包的意識,最好有工具能讓開發在日常開發中清楚知道每個檔案/子產品的大小,切實感受到需求內建後對整體包大小的影響和相關檔案/子產品變動情況,進而促進開發進行相應的優化。
3.1 增量自動分析
通過将前面介紹的包分析能力內建到打包腳本,在每次包建構成功時,也會同步産出基礎的包内容資訊,再通過進一步的分析後獲得包中每個檔案/子產品的大小情況。當代碼改動觸發重新打出新包後,檔案/子產品通過一一對比的方式,找出哪些有新增,哪些被删除,哪些内容發生變動,以及變動産生的大小,并産出對比報告郵件。通過這樣的方式讓開發對代碼增量有一個直覺感受。
那如何讓包增量分析工具能在日常開發中持續穩定發揮作用呢,接下來介紹閑魚的需求增量卡口設計。
3.2 增量卡口設計
在之前,閑魚的包大小差異通常都在拉出內建分支,打出版本release包時才發現,經常會震驚于這個版本的包又比上一個版本要大幾M,然後再緊急去尋找是什麼需求內建導緻的巨大增量。但這時發現包大小的問題已經非常滞後了,版本馬上就要釋出,這個時候即使抓到了劇增的源頭,也很難在短時間内進行優化。
是以需要增加需求內建卡口,測試通過後在合入主分支之前,經過包增量确認再內建,而不是在內建後打出release包時。現在的做法如下,開發隻需要送出代碼,即可自動獲得包增量分析報告。
其中包增量對比郵件内容,會包含與主分支最新建構、目前分支前一次建構,目前分支最初一次建構包的包大小和增量的對比結果。此外為了資料的準确性,需要開發在拉出開發分支後先建構一個基準包,并在提測和內建前合并一把主幹,這樣報告資料才會更準确。
最後是提測部分,開發同學發送提測郵件時需要标注本次提測包增量及圖檔壓縮情況,若需求增量大于100K,根據超出範圍情況,需要備注原因和老闆确認。bug修複期間不免也會有代碼改動,在測試完成後內建前,會再确認一次包增量情況再內建。
4.效果與展望
閑魚需求增量卡6.20正式上線至今半月,7個用戶端新需求都收到了嚴格的卡口洗禮。以iOS為例,這半個月主幹分支包大小不增反降0.5M,在開發過程中,開發也開始有意識通過優化老業務代碼和資源,為新需求增量挪出空間。
需求增量卡口隻是長效控制包大小的一個手段,加強開發在日常編碼中的瘦包意識,讓每一次需求都有迹可循,此外對于存量的資源、業務代碼清理等手段也在有序進行,flutter産物更精細的分析方案和有效的瘦身方法也在持續探索中,期待小胖魚早日瘦身成功吧~