首頁>Club>
7
回覆列表
  • 1 # 位元組跳動技術團隊

    Apk包大小是Android最佳化的一項重要指標,本文主要從資源方面著手,提出一些最佳化的新思路。

    無用資源精簡

    專案隨著開發迭代,會遺留大量的無用程式碼和資源,今天主要說一下無用資源如何精簡。

    資源精簡最重要的是無用資源的檢索,常規檢索方式有lintunused resourcegradle開啟shrinkResources。但用lint僅僅檢測出了十幾個,效果不明顯,開啟shrinkResources後,對包大小的影響也在幾K級別。Google把shrinkResources整合到了打包流程中,為什麼很多無用資源都檢索不出來呢,帶著這些疑問,我決定仔細研究一下原理。

    開啟shrinkResources後,打包過程會新增task transformClassesWithShrinkResFor{variant}gradle1.5之後只需要註冊一個tranform就會產生一個對應的task,檢視原始碼發現對應的tranfromcom.android.build.gradle.internal.transforms.ShrinkResourcesTransform,此類中呼叫com.android.build.gradle.tasks.ResourceUsageAnalyzer的analyze方法進行分析無用資源。

    該方法首先會根據R檔案來生成資源表,然後利用asm遍歷所有的class分析class中用到的R,然後將資源表中相應資源標記為可達,接著分析MainfestRes檔案找出資原始檔中引用的其它資源。最終呼叫keepPossiblyReferencedResources,此方法是標記可能會引用的資源,專案中呼叫了getIdentifier,引數是萬用字元,資源名稱只要匹配就會標記為可用。

    檢視編譯後的檔案build/outputs/mapping/release/resources.txtshrinkResources相關的日誌都會在此檔案中,有大量資源因為keepPossiblyReferencedResources被標記為可達。

    從原理上分析,這種查詢無用資源的方式是可行的,只是需要稍微改動。預設情況下shrinkResources是安全模式,可能會被使用的資源也被標記為了可達;關閉安全模式,開啟嚴格模式,只有真正透過程式碼或是資原始檔引用的資源才被標記為可達。混淆配置新增-dontshrink -dontoptimize,系統是分析混淆後的類,如果一個類被壓縮掉了,它引用的資源就會被標誌為不可達,這時候如果僅僅刪除資源,後續就編譯通不過了。

    res目錄中新增keep.xml,設定嚴格模式。

    重複資源精簡

    Android開發推崇根據業務拆分多個模組,模組間為了防止資源覆蓋,會給每一個模組的資源加一個字首,同樣的資源就會在apk包中出現多次。閱讀微信資源混淆原始碼時發現,它將每個資源Chunk中的資源路徑替換為了一個較短的路徑。那麼對於重複資源,僅僅保留一份,修改arsc檔案,重定向Chunk對應的資源路徑,就可以解決重複資源問題。

    打包過程中ProcessAndroidResources這個Task會生成資原始檔/build/intermediates/res/resources-release.ap_,該檔案是一個zip檔案,解壓後包括AndroidManifest.xml,res,resources.arsc幾部分。res目錄中的檔案即是最終要打入到apk中的,resources.arsc即為最終的arsc檔案。

    解壓ap_檔案,遍歷res目錄中的檔案,根據每個檔案的md5值,找出重複的檔案。最終發現主要有兩種重複的情況,一種是檔名相同,但在不同的dpi目錄下;一種是內容相同,名字不同。刪除重複檔案,保留一份,然後利用ChunkUtil這個庫來修改arsc檔案,ChunkUtil是一個arsc編輯庫。

    重複資源處理,作為一個gradle外掛,後續會開源給大家作為參考。

    重複資源處理後,資源對映如下所示,每個資原始碼一個chunk,假如以下3個chunk中的資源相同,則處理後它們會指向相同的路徑。

    根據錯誤堆疊可以定位到微信資源混淆出現的位置。閱讀微信資源混淆原始碼,發現對映關係如下:從res/drawable/icon.pngr/a/c.png是一一對映的。每個資源路徑可變的有兩部分,一是資源型別如drawablecolor;另一個是資源名稱。對映過程有以下規則,同類型資源會對映到相同目錄中,資源id相同也即是同名資源對映後的資源名相同。如下圖中,資源1和2是名字相同,對映後的名字都是c.png,資源2和資源3資源型別相同,對映後的資源都在r/b目錄下。

    資源1和資源2對映後的目標路徑相同。微信資源混淆會遍歷每個chunk,把每個chunk中的資源從原始目錄copy到目標目錄並重新命名為對映後的檔案,copy前check目標檔案是否存在,如果存在會出現上述錯誤。

    微信資源混淆的目標路徑對映規則是根據id值對映的,不是根據原始路徑。因此我們需要改變預設的對映規則,如果原始路徑相同,則對映到相同的目標路徑,並且不做後續的copy工作。修改了對映邏輯後,資源3最終對映的路徑也變為了r/a/c.png

    重新打包,發現還是出現上述錯誤,只是出錯的資源不同。這時候如果有第四個資源,和前面3個資源內容不相同。資源型別和資源1相同,所以對映成了r/a/目錄,名稱和資源3相同,所以最終對映成了r/a/c.png,又導致了上述目標地址重複的問題。這種情況需要對路徑進行remapping

  • 中秋節和大豐收的關聯?
  • 絕地求生轉會期盤點,4AM試訓神秘選手欲再登巔峰,Mad98重新連線加盟SMG,如何評價?