Android App安裝包瘦身計劃

来源:https://www.cnblogs.com/mengdd/archive/2019/07/26/reduce-size-of-android-app.html
-Advertisement-
Play Games

Android App安裝包體積優化: 理由, 指標和可以採用的方法. ...


Android App安裝包瘦身計劃

Android App安裝包體積優化: 理由, 指標和可以採用的方法.

本文內容歸納如下圖:
reduce apk size

為什麼要安裝包瘦身

  • 安裝包需要瘦身嗎?
  • 不需要嗎?

安裝包要瘦身的主要原因就是考慮應用的下載轉化率和留存率.
應用太大了, 用戶可能就不下載了, 尤其是移動網路或者流量收費的情況下.
再者, 因為手機空間問題, 用戶有時候可能需要選擇卸載一些應用, 就會先盯上那些占空間大的, 所以應用大小也會也影響留存率.

但現在什麼時代了, 到處都是WiFi, 流量包又大又便宜, 網速快到飛起, 手機硬體也在不斷升級, 更大更快更高級, 安裝包真的還需要瘦身嗎? 有人在乎嗎?

那當然還是有一定必要的, 不然這篇文章寫到這裡已經結束了.
因為就算用戶不在乎你這個應用有多少大, 決定要下載安裝了, 安裝包越大, 下載時間就會越長, 沒等下載完成就因為各種原因取消或者斷網了.
或者因為下載時間稍微有點長, 等完成以後, 用戶已經忘了要用這個app了.

現代人的耐心很有限, 不然也不會有小程式這樣輕量級的解決方案.

安裝包瘦身可以地定量, 在一定程度上改善問題.

除了下載下載轉化率和留存率, 安裝包體積優化還有一些理由:

  • 預裝應用的推廣成本.
  • 滿足應用市場的最大包體積限制.
  • 在技術甚至業務層面, 我們可以重新審視我們的codebase, 是否需要刪除一些低價值的業務, 清理無用的代碼和資源, 進行進一步的重構和改善.

安裝包的構成

Apk安裝包本質上是一個zip壓縮文件, 解壓之後我們可以看到其中包含的內容, 通常包含:

  • 一個或多個classes.dex文件: 代碼編譯結果.
  • assets/文件夾.
  • resources.arsc: 編譯後的二進位資源文件, 包含了所有res/values中的XML內容, 如字元串, style等, 也包括對沒編譯進來的資源(比如layout和圖片)的路徑.
  • res/: 沒有被編譯進resources.arsc文件的資源: layout, drawable等.
  • AndroidManifest.xml: merge後的manifest文件.
  • META-INF/: 包含簽名文件等.

如果有native的庫, 可能還會有lib/文件夾.

尺寸指標

安裝包大小有幾個指標呢?
有三個:

  • 文件尺寸: 這可能是最直觀的一個, 就是我們build出來的文件大小. 但是其實它是最不重要的一個指標.
  • 下載尺寸: 下載app需要的大小, 最重要的指標. 因為下載市場(Google Play)會幫我們做優化, 下載尺寸會比文件的尺寸小.
    一般增量安裝和全新安裝需要的大小是不一樣的.
  • 安裝尺寸: app安裝到手機上, 啟動解壓之後的大小, 關係到app的留存率.

安裝包分析工具

在做安裝包體積優化之前, 建議用工具看一下當前的安裝包情形, 找到當前的bottle neck, 這樣才能有的放矢.
優化之後也好做一個比較.

APK Analyzer

APK Analyzer是Android Studio自帶的工具.
使用APK Analyzer的三種方法:

  • Build > Analyze APK.., 選擇.apk文件.
  • 切換到Project View, 雙擊build/outputs/apk目錄下的apk文件.
  • 把apk文件拖到Android Studio的編輯視窗中.

利用這個工具可以查看apk的各個組成部分以及它們各自的大小, 包括文件大小和預估的下載大小.

這個工具還可以比較兩個APK, 從而可以看出具體是哪個部分有尺寸的變化.

官方插件: Android Size Analyzer

在Android Studio中可以添加Plugin: Android Size Analyzer.

安裝之後重啟Android Studio, 接著就可以點擊菜單Analyze > Analyze App Size.
點擊之後會給出建議, 顯示出項目中比較大的文件, 建議轉化一些圖片到webp格式, 建議開啟代碼壓縮混淆等.

安裝包瘦身措施: Coding階段

減少代碼(native和Java代碼)

  • 控制第三方庫的使用: 依賴第三方庫的時候, 儘量減小範圍, 或者使用更加友好的替代. (ProGuard會移除不用的代碼, 但是它不會移除庫的內部依賴.)
  • 減少不必要的生成代碼.
  • 減少枚舉的使用.
  • 減小native庫: 移除debug symbols和關閉so庫壓縮.

控制資源使用

要控制資源的使用: 減少資源的數量, 減小資源的大小.

設備屏幕支持

Android有很多屏幕密度類型:

ldpi, mdpi, tvdpi, hdpi, xhdpi, xxhdpi and xxxhdpi

並不是需要每個資源都提供多密度版本的.
當一個屏幕密度下沒有提供特定的資源, Android會自動根據這個資源其他密度的版本進行縮放.

如果你的app僅需要提供縮放圖像, 你可以只在drawable-nodpi中提供單個資源. (建議還是至少有一個xxhdpi.)

shape的使用

很多時候我們並不需要一個圖片, 比如純色或漸變色的背景, 帶邊框, 帶圓角等.

<shape>尺寸就會比較小, 也不用多個密度的版本.

復用資源

這裡的復用可以是改變了顏色和旋轉方向等的復用.
比如改資源的tint, 或者把一個圖旋轉了之後再用. (thumb up變成thumb down了).

壓縮圖片

可以用一些工具對PNG和JPEG圖片進行無損壓縮: 圖片質量無損但是尺寸變小.

使用WebP格式的圖片

可以使用WebP格式的圖片, 比JPEG和PNG壓縮得更好.

Android Studio提供了轉換工具: Create WebP images

註意: Google Play只接受PNG作為launcher icon.

使用矢量圖

可以使用矢量圖來作為可伸縮的資源, 在Android中是VectorDrawable對象.

但是矢量圖的渲染需要系統的時間花銷, 所以推薦只有小的icon使用矢量圖.

逐幀動畫

不要再使用很多個圖片逐幀播放來實現一個動畫效果了.
儘量用改變屬性的動畫來節省資源使用.

另外還有一些手段比如在程式中繪製, 而不是使用圖片.

安裝包瘦身措施: Post-coding階段

Lint靜態分析

Lint是靜態分析工具, 會提示項目中的各種問題.

針對不用的資源, 可以專門這樣檢查:
在Android Studio中: Analyze -> Run Inspection by Name... -> Unused Resources.

掃描後會對所有未使用的資源給出警告.

註意, 因為是Lint靜態檢查, 所以會有一些錯報和漏報的情況:

  • assets/目錄不被掃描.
  • 檢測不到第三方庫中帶來的不用的資源.
  • 錯報: 資源在項目中實際上用了, 但是被檢測出來了. 如果資源是被動態引用的, 比如用getIdntifer()方法, 拼名字使用, 會被lint錯誤地報告說沒有用到.
  • 漏報: 實際上沒有用到, 但是沒有檢測出來. 這是因為還有另一個沒有用到的代碼引用了這個資源.
    比如有一個沒有人用的Fragment, 它的佈局文件就不會被檢測出來, 因為寫在了代碼里. (註意: 如果是不用的佈局中include了另一個佈局, 這兩個佈局都能被檢測出來.)

靜態檢查只是幫我們檢測並報告, 真正要移除這些資源還得靠我們手動刪除.

代碼壓縮和混淆

ProGuard -> R8會幫我們刪除不用的代碼, 進行名稱改寫和代碼優化.

minifyEnabled true

不用的代碼被移除了, 類和成員的名稱都被改得很短, 從而有效地減小了dex文件的大小.
這裡需要註意rules的編寫, 不要過度保護.

比較追求極致的方法會從這個方面下手, 進一步細化優化ProGuard Rules, 以達到更深度的混淆和壓縮.

shrinkResources

移除不用資源的一個有利工具shrinkResources:

android {
    // Other settings

    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

為了開啟壓縮資源, 必須先開啟壓縮代碼, 即minifyEnabled要為true.
這樣, 在打包的時候, 首先移除了不用的代碼, 然後Gradle才能檢測哪些資源還在被代碼引用, 從而移除不用的資源.

註意這個過程並沒有真的刪除掉資源, 只是將不用的資源替換了一個簡單版本.

跟代碼壓縮一樣, 資源壓縮也可以指定一個自定義的規則, 明確指明哪些資源要保留.

註意開啟了代碼壓縮和混淆之後, 編譯速度會變慢, 所以一般debug版本不開啟.

這種刪除方式靠譜嗎? 精確嗎? 會誤刪嗎?

常規情況下resource shrinker還挺準確的.

但是如果你的代碼(或依賴的庫)中有用到Resources.getIdentifier()方法來獲取資源, (比如AppCompat中這麼做), 那麼說明你的代碼會根據一個動態生成的字元串來查找資源.

如:

val name = String.format("img_%1d", angle + 1)
val res = resources.getIdentifier(name, "drawable", packageName)

這個時候resource shrinker預設會表現得很保守, 會保留所有符合這個名字模式的資源, 認為它們都是可能會被用到的, 從而不會刪除它們.

而且resource shrinker還會掃描字元串, 查找符合資源格式的URLs, 比如file:///android_res/drawable//ic_plus_anim_016.png, 從而保留這些對應的資源.

所以不用擔心, 預設情況下, 資源壓縮都是"better safe than sorry"的模式.

如果你想激進一點, 可以在keep.xml裡面把壓縮模式指定為shrinkMode="strict", 這時候你就必須手動指定需要keep的動態使用的資源了.

如何查看都刪了什麼呢?

用命令行:

./gradlew clean assembleRelease --info | grep "Skipped unused resource"

註意這裡assembleRelease可以替換成任何一個開啟了資源壓縮的type.

執行了之後會列出所有被替換成dummy file的資源.

用IDE:
在Android Studio的Build視窗下, Toggle View切換到詳細信息模式下. Build, 然後查找信息, 可以看見信息中會有一句:

Removed unused resources: Binary resource data reduced from XXXKB to YYYKB: Removed Z%

顯示資源壓縮節省出來的大小.

文件:
在app的輸出路徑(<module-name>/build/outputs/mapping/release/)下, 和mapping.txt一起, 還有一個resources.txt文件.
文件的最後會列出一系列的Skipped unused resource.

同時這個文件里也詳細地寫了資源之所以會被保留下來的理由. 關鍵字: matches string pool. 這是前面說過的為了防止有動態使用資源的情況而採取的保守措施.

如果有空的話可以把這些Skipped unused resource標註的文件都手動從代碼庫里刪除.

聲明支持的配置, 刪掉其他

Gradle的resource shrinker會刪除沒有被代碼用到的資源, 但是不會刪掉不同設備配置下的可替代資源
(alternative resources).

可以利用resConfigs, 來刪除你的app並不需要的配置資源.

原理就是聲明app支持的配置, 這樣其他不支持的配置的資源就會被刪除, 從而減少apk的尺寸.

比如你包含了一個庫, 這個庫中帶有各種語言的資源, 這樣預設你的app就會包含所有這些庫中包含的語言的字元串. 你可以明確聲明app支持的語言, 那麼其他語言資源就會被刪除:

android {
    defaultConfig {
        ...
        resConfigs "en", "fr"
    }
}

Remove unused alternative resources

安裝包瘦身措施: 發佈階段

因為有的資源會提供多個版本, 所以一個用戶下載的apk中可能包含了一些他永遠也用不到的內容.

為了保證每個用戶的下載尺寸都儘量小, 我們可以改進發佈包的形式: 拆分成多個針對不同機型的apks;
如果是在Google Play發佈, 還可以用App Bundle, 由Google Play幫我們做拆分.

Build多個apks

根據屏幕密度和指令集(ABI)的不同, 可以拆分多個apk, 每種配置只下載自己需要的資源, 從而達到減小安裝包大小的目的.

Gradle就可以幫我們做這個拆分創建的工作.
具體實現見:
Build multiple APKs.

Android App Bundles

用Google Play發佈應用時可以使用Android App Bundle的格式, 這是一種新的發佈格式, 包含了應用所有的編譯代碼和資源, 把apk的生成和簽名留給Google Play來做.

Google Play會根據用戶不同的設備配置來生成優化的apk, 這樣用戶下載的就只是對應自己設備的代碼和資源. 開發者不用為不同設備準備多個apk, 用戶也得到了更小更優化的結果.

安裝包瘦身措施: 進階補充

要更加極致地壓縮安裝包, 還有一些更加複雜的手段, 比如:

  • 業務層面: 動態feature, 資源動態載入.
  • Dex: 加大混淆力度, 混淆四大組件和View; 去除debug信息; 優化Dex分包; Dex壓縮.
  • native庫: 去除debug信息; 壓縮; 剪裁.
  • 資源: 資源名混淆成短路徑; 壓縮.

另外, 還可以將對安裝包體積的分析加入到CI中, 進行自動化監控, 這樣可以得到各個版本的尺寸變化, 及時獲知是否不經意引入大的依賴, 引入過大的資源等情況.

總結

總結回顧一下本文內容, 對於安裝包瘦身的話題:
首先, 討論了我們為什麼要做這個事情 -> 為了增加app被下載安裝使用和留存的幾率. (為什麼要瘦身呢? -> 為了提升自己增加競爭力.)
其次, 介紹了安裝包的構成, 相關的指標和用於檢測的工具. (瘦身需要關註的指標和測量方式.)
重點介紹, 如何減少安裝包體積的實踐方法. (瘦身的各種手段.)

參考資料

最後, 歡迎關註微信公眾號: 聖騎士Wind
微信公眾號


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 對於信息安全有要求的,在數據下發和同步過程中需要對含有用戶身份信息的敏感欄位脫敏,包括用戶姓名、證件號、地址等等,下麵是自定義函數的代碼CREATE OR REPLACE FUNCTION F_GET_SENSITIVE(IN_STR VARCHAR, IN_TYPE NUMBER) RETURN ... ...
  • 還原資料庫的時候明明選擇了備份集,還是提示未選擇還原的備份集 後來查了下,是因為我本地有兩個資料庫(2008R2和2014),對應的兩個資料庫實例。而還原bak是sqlserver2014的備份,我預設登的是2008的實例,所以一直有問題。 怎麼查看資料庫的實例名:https://zhinan.so ...
  • EOMONTH()查詢日期數據所屬月數的最後一天; YEAR(COL)輸出日期數據的年份、month(col)輸出日期數據的月份; 查詢數據時in與exist的區別:in是先查詢條件,然後只查詢一次條件。而exists是先運行select語句,查詢出所有的數據以後在運行條件語句,多次查詢,查詢的次數 ...
  • SQL註入1 題目 訪問題目網址 先查看一下源碼 仔細分析一下核心源碼 通過分析源碼知道了 的值為 ,因為sql查詢語句里有and,必須and前後同時成立才可以查詢,但是現在不知道 對應的值,所以考慮能不能不判斷pass,直接判斷user,於是想到是不是可以將user判斷語句閉合併註釋後面的內容,這 ...
  • 我們先看張gif圖看一下效果(LICEcap錄製的有點卡, 湊合看) 好像還是卡, 懟個視頻演示鏈接吧: https://m.weibo.cn/1990517135/4398431764047996 我們先來分析一下頁面結構, 然後分析具體動畫實現. 頁面結構: 可以將當前頁面和下個頁面復用, 下個 ...
  • 1、項目的目錄結構能看出你的開發經驗 2、iOS工程目錄結構的思考 ...
  • 今天介紹Mac的截圖功能,如果你以為Shift + Command + 3 / 4就是Mac全部的截屏功能的話,那你小看Mac了。它遠比你想得強大。 1.Shift + Cmd + 3 ——截取全屏 2.Shift + Cmd + 4 直觀地拖動滑鼠截屏: 按一下空格鍵: 以下操作都是在Shift ...
  • CHM格式是1998年微軟推出的基於HTML文件特性的幫助文件系統。以替代早先的winHelp幫助系統,它也是一種超文本標識語言。在Windows 98中把CHM類型文件稱作“已編譯的HTML幫助文件”。 CHM用了ITS文件壓縮格式,使得文件體積大大減小。事實上CHM文件不僅可以包含HTML文件, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...