大數據

欲瘦其包,必先探清其底細

作者:閒魚技術-金喏

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包中的內容,從文件類型角度分析包資源佔比情況,以及將資源文件按照大小排序展示,並以圖表形式直觀告訴開發資源情況。
android分析結果.png

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的資源。分析結果如下圖所示:
iOS分析結果.png

3.需求增量卡口

  瘦包主要靠開發努力,要讓大家在平時開發中有瘦包的意識,最好有工具能讓開發在日常開發中清楚知道每個文件/模塊的大小,切實感受到需求集成後對整體包大小的影響和相關文件/模塊變動情況,從而促進開發進行相應的優化。

3.1 增量自動分析

  通過將前面介紹的包分析能力集成到打包腳本,在每次包構建成功時,也會同步產出基礎的包內容信息,再通過進一步的分析後獲得包中每個文件/模塊的大小情況。當代碼改動觸發重新打出新包後,文件/模塊通過一一對比的方式,找出哪些有新增,哪些被刪除,哪些內容發生變動,以及變動產生的大小,併產出對比報告郵件。通過這樣的方式讓開發對代碼增量有一個直觀感受。
  那如何讓包增量分析工具能在日常開發中持續穩定發揮作用呢,接下來介紹閒魚的需求增量卡口設計。

3.2 增量卡口設計

  在之前,閒魚的包大小差異通常都在拉出集成分支,打出版本release包時才發現,經常會震驚於這個版本的包又比上一個版本要大幾M,然後再緊急去尋找是什麼需求集成導致的巨大增量。但這時發現包大小的問題已經非常滯後了,版本馬上就要發佈,這個時候即使抓到了劇增的源頭,也很難在短時間內進行優化。
  因此需要增加需求集成卡口,測試通過後在合入主分支之前,經過包增量確認再集成,而不是在集成後打出release包時。現在的做法如下,開發只需要提交代碼,即可自動獲得包增量分析報告。
流程圖.png
  其中包增量對比郵件內容,會包含與主分支最新構建、當前分支前一次構建,當前分支最初一次構建包的包大小和增量的對比結果。此外為了數據的準確性,需要開發在拉出開發分支後先構建一個基準包,並在提測和集成前合併一把主幹,這樣報告數據才會更準確。
郵件.png
  最後是提測部分,開發同學發送提測郵件時需要標註本次提測包增量及圖片壓縮情況,若需求增量大於100K,根據超出範圍情況,需要備註原因和老闆確認。bug修復期間不免也會有代碼改動,在測試完成後集成前,會再確認一次包增量情況再集成。

4.效果與展望

  閒魚需求增量卡6.20正式上線至今半月,7個客戶端新需求都收到了嚴格的卡口洗禮。以iOS為例,這半個月主幹分支包大小不增反降0.5M,在開發過程中,開發也開始有意識通過優化老業務代碼和資源,為新需求增量挪出空間。
  需求增量卡口只是長效控制包大小的一個手段,加強開發在日常編碼中的瘦包意識,讓每一次需求都有跡可循,此外對於存量的資源、業務代碼清理等手段也在有序進行,flutter產物更精細的分析方案和有效的瘦身方法也在持續探索中,期待小胖魚早日瘦身成功吧~

Leave a Reply

Your email address will not be published. Required fields are marked *