碼個蛋(codeegg) 第 945 次推文
作者:jsonchao連結:https://juejin.im/post/5e7ad1c0e51d450edc0cf053
複習上篇:《深入探索 Android 包瘦身(上)》
資源瘦身方案探索
衆所周知,Android建構工具鍊中使用了AAPT/AAPT2工具來對資源進行處理,Manifest、Resources、Assets 的資源經過相應的 ManifesMerger、ResourcesMerger、AssetsMerger 資源合并器将多個不同 moudule 的資源合并為了 MergedManifest、MergedResources、MergedAssets。然後,它們被 AAPT 處理後生成了 R.java、Proguard Configuration、Compiled Resources。如下圖左上方所示:
其中 Proguard Configuration、Compiled Resources的作用如下所示:
- Proguard Configuration:這是AAPT工具為Manifest中聲明的四大元件與布局檔案中使用的各種Views所生成的混淆配置,該檔案通常存放在
。${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/proguard-rules/${flavorName}/${buildType}/aapt_rules.txt
- Compiled Resources:它是一個Zip格式的檔案,這個檔案的路徑通常為
。在經過zip解壓之後,可以發現它包含了res、AndroidManifest.xml和resources.arsc 這三部分。并且,從上面的APK建構流程中可以得知,Compiled Resources會被apkbuilder打包到APK包中,它其實就是APK的資源包。是以,我們可以通過 Compiled Resources 檔案來修改不同字尾檔案資源的壓縮方式來達到瘦身效果的。但是需要注意的是,resources.arsc 檔案最好不要壓縮存儲,如果壓縮會影響一定的性能,尤其是在冷啟動時間方面造成的影響。并且,如果在 Android 6.0 上開啟了 android:extractNativeLibs=”false” 的話,So 檔案也不能被壓縮。${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/res/resources-${flavorName}-${buildType}-stripped.ap_
1、備援資源優化
1、使用 Lint 的 Remove Unused Resource
APK的資源主要包括圖檔、XML,與備援代碼一樣,它也可能遺留了很多舊版本當中使用而新版本中不使用的資源,這點在快速開發的App中更可能出現。我們可以通過點選右鍵,選中Refactor,然後點選Remove Unused Resource => preview可以預覽找到的無用資源,點選Do Refactor可以去除備援資源。如下圖所示:
需要注意的,Android Lint 不會分析 assets 檔案夾下的資源,因為 assets 檔案可以通過檔案名直接通路,不需要通過具體的引用,Lint 無法判斷資源是否被用到。
2、優化 shrinkResources 流程真正去除無用資源
resources.arsc中可能會存在很多無用的資源映射,我們可以使用 android-arscblamer,它是一個指令行工具,能夠解析 resources.arsc 檔案并檢查出可以優化的部分,比如一些空的引用。
此外,當我們通過 shrinkResources true來開啟資源壓縮,資源壓縮工具隻會把無用的資源替換成預定義的版本而不是移除。那麼,如何高效地對無用資源自動進行去除呢?
我們可以 在 Android 建構工具執行 package${flavorName}Task 之前通過修改 Compiled Resources 來實作自動去除無用資源,具體的實作原理如下:
1)、首先,收集 Compiled Resources 中被替換的預定義版本的資源名稱
通過檢視 Zip 格式資源包中每個 ZipEntry 的 CRC-32 checksum 來尋找被替換的預定義資源,預定義資源的 CRC-32 定義在 ResourceUsageAnalyze 中,如下所示:
// A 1x1 pixel PNG of type BufferedImage.TYPE_BYTE_GRAY public static final long TINY_PNG_CRC = 0x88b2a3b0L; // A 3x3 pixel PNG of type BufferedImage.TYPE_INT_ARGB with 9-patch markers public static final long TINY_9PNG_CRC = 0x1148f987L; // The XML document as binary-packed with AAPT public static final long TINY_XML_CRC = 0xd7e65643L;
2)、然後,使用 android-chunk-utils 把 resources.arsc 中對應的定義移除。
3)、最後,删除資源包中對應的資源檔案即可。
2、重複資源優化
在大型 App項目的開發中,一個App一般會有多個業務團隊進行開發,其中每個業務團隊在資源送出時的資源名稱可能會有重複的,這将會引發資源覆寫的問題,是以,每個業務團隊都會為自己的資源檔案名添加字首。這樣就導緻了這些資源檔案雖然内容相同,但因為名稱的不同而不能被覆寫,最終都會被內建到APK包中。這裡,我們還是可以在 Android 建構工具執行 package${flavorName}Task 之前通過修改 Compiled Resources 來實作重複資源的去除,具體放入實作原理可細分為如下三個步驟:
- 1)、首先,通過資源包中的每個ZipEntry的CRC-32 checksum來篩選出重複的資源。
- 2)、然後,通過android-chunk-utils修改resources.arsc,把這些重複的資源都重定向到同一個檔案上。
- 3)、最後,把其它重複的資源檔案從資源包中删除,僅保留第一份資源。
具體的實作代碼如下所示:
variantData.outputs.each { def apFile = it.packageAndroidArtifactTask.getResourceFile; it.packageAndroidArtifactTask.doFirst { def arscFile = new File(apFile.parentFile, "resources.arsc"); JarUtil.extractZipEntry(apFile, "resources.arsc