Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> [Android 之美] 那些你不知道的APK 瘦身,讓你的APK更小

[Android 之美] 那些你不知道的APK 瘦身,讓你的APK更小

編輯:關於Android編程

讓你的apk文件盡可能小,應該使移除未使用的代碼和資源文件。那麼本章節介紹了如何做到讓APK更小,性能更好,下載轉化率會更高,以及如何指定在構建APK過程中保留或移除的代碼和資源,在我們還沒有分析APK大小之前,項目中存在一些資源放置處理不當,沒有統一的規范,依賴管理不合理,資源重疊,dex方法數過多等問題,導致APK文件比較大,公司要求APK體積大小要優化到3M左右。經過我們的努力終於達到要求,然而我們發現還能再小。

分析APK的大小

正所謂工欲善其事,必先利其器,我們得現有利器,下面就是我們常用的分析APK大小工具的利器。

使用Android Studio 2.2

Android Studio 2.2 新功能直接能分析APK的大小,雙擊打開就能看到那些占用APK比例大,方法數等。

這裡寫圖片描述

分析任何的APK 查看APK下載包的大小,解壓後的實際大小 反編譯資源文件,還原layout中的資源id,代碼 分析dex,顯示每部分的方法數,直接查看那些library體積比較大

使用方法:Build -> Analyz APK

有了Analyz APK這個利器,以下工具也可以基本不用了<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMyBpZD0="nimbledroid">NimbleDroid

NimbleDroid 是美國哥倫比亞大學的博士創業團隊研發出來的自動化分析Android app性能指標的系統,分析的方式有靜態和動態兩種方式,其中靜態分析可以分析出APK安裝包中大文件排行榜,各種知名SDK的大小以及占代碼整體的比例,各種類型文件的大小以及占排行,各種知名SDK的方法數以及占所有dex中方法數的比例,針對緩慢的方法,緩慢的第三方SDK和內存洩漏。
測量生成的速度、網絡、內存和磁盤使用率。總之有非常多分析App性能的功能,如果要做性能優化,也可以嘗試使用NimbleDroid。

查看詳細的方法耗時

\

具體使用方法請看官網:
https://nimbledroid.com/

ClassShark

ClassShark 是一款查看Android執行文件(apk)的浏覽工具,目前有兩個android App(Apk)和桌面(jar)的版本。
使用這款工具,可以很方便的打開APK/Class/Jar/res
等 文件和分析裡面的內容。

\

具體源碼與使用方法詳細在github中:
https://github.com/google/android-classyshark

通過以上任一工具分析我們知道我們項目中主要是以下文件占用APK大小:

classes.dex
classes.dex是java源碼編譯後生成的java字節碼文件,

res
主要是存放我們的圖片資源

resources.arsc
編譯後的二進制資源文件,非常多無效資源文件(語言)

assets
主要存放了我們的緩存數據文件,已做最優化壓縮,我們考慮能否雲端存放。

lib
主要是存放我們的so庫,目前我們已經優化了

既然知道了那些數據導致我們APK體積大,那麼我們就著手瘦身了。

對APK進行瘦身

對資源進行極限壓縮

對資源進行極限壓縮,主要是如res裡面用到的圖片資源文件和assets的html,db等一些緩存預留在APK的數據文件

assets資源壓縮,使用7zip或者lzma壓縮方式最高 res 圖片資源的壓縮,使用tinypng優化Android的資源圖片,通常我們可以在保證圖片不失真的情況下,多壓縮幾次。目前tinypng已經支持png和jpg圖片、.9圖的壓縮 將非alpha的圖轉換成jpg形式

通過以上方法我們圖片降低了79%的大小。

使用WEBP,SVG圖片資源格式

WebP是谷歌研發出來的一種圖片數據格式,它是一種支持有損壓縮和無損壓縮的圖片文件格式,如果應用支持到Android 4.0+,那麼我們可以使用WebP格式代替PNG,我們的資源大小能降低50%多。或者有些資源可以使用SVG圖片資源更小。
以下是他們的對比圖:

這裡寫圖片描述

這裡提供方便轉換的WEBP資源的工具:

https://isparta.github.io/

https://imageoptim.com/mac

使用VectorDrawable和SVG圖片來替換原有圖片
使用SVG不用考慮屏幕適配問題,體積非常小。

利用AndResGuard資源壓縮打包工具

微信中的資源混淆工具主要為了混淆資源ID長度(例如將res/drawable/icon.png,png變成混淆為r/s/a.png),同時利用7z深度壓縮、對png的存儲方式做了改變占用內存更小,大大減少了安裝包體積

具體源碼與使用方法詳細在github中:

https://github.com/shwenzhang/AndResGuard

清除你的代碼和資源

通過上面的圖片資源壓縮能對APK減小不少,但這往往還不夠,項目裡還有很多未使用的資源文件,重復的資源等,這裡主要參考Google官方文檔https://developer.android.com/studio/build/shrink-code.html 部分,利用Android Plugin開啟gradle 的Code shrinking和ProGuard結合使用。

ProGuard能夠檢測和刪除未使用的類,字段,方法,和從你的打包應用程序的屬性,包括那些包含代碼庫,ProGuard是一個混淆優化字節碼的工具,能夠刪除一些未使用的代碼,混淆使用的類,字段,方法和短名稱,經過混淆處理也能夠使APK源代碼得到保護

Code shrinking是一個Android Plugin for Gradle,從您的打包的應用程序中刪除未使用的資源,包括代碼庫中的未使用的資源。它工作在與代碼縮小,這樣,一旦未使用的代碼已被刪除,任何資源不再引用可以安全地刪除。

該功能需要依賴於:

SDK Tools 25.0.10 或更高 Android Plugin for Gradle 2.0.0 或更高

清除無用代碼

code shrinking需要結合ProGuard使用,添加minifyEnabled true在你的build.gradle文件中。

需要注意code shrinking會減慢Gradle 編譯,應避免使用它在您的調試版本中使用它。Android Studio禁用ProGuard使用 Instant Run.。
例如,以下從build.gradle文件片段,使code shrinking為發布版本

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

其中getDefaultProguardFile(‘proguard-android.txt')默認ProGuard設置來自於Android SDK tools/proguard/中的文件夾

更多的代碼減少可以嘗試使用相同位置的proguard-android-optimize.txt文件(這裡我們又減少了0.5M)proguard-rules.pro是你自定義的proguard規則。

每一次build,ProGuard會輸出以下文件在/build/outputs/mapping/release/:

dump.txt
描述.apk文件中所有類文件間的內部結構 mapping.txt
列出了原始的類,方法和字段名與混淆後代碼間的映射。這個文件很重要,當你從release版本中收到一個bug報告時,可以用它來翻譯被混淆的代碼。 seeds.txt
列出了未被混淆的類和成員 usage.txt
列出了從.apk中刪除的代碼 resources.txt
列出resource被保留的資源
自定義混淆規則

在某些情況下,默認的混淆器配置文件proguard-android.txt文件是會移除所有只有未使用的代碼,但也有可能會誤刪除了你需要的代碼,所以要注意以下幾種情況:

在AndroidManifest.xml配置的文件類 使用了JNI 的接口方法 運行時反射調用方法(不過現在ProGuard已經可以處理這種了)

添加-keep來忽略一下防止被混淆的代碼到proguard-rules.pro文件中,比如:

-keep public class MyClass

另外也可以使用@Keep 注解在你的需要忽略的代碼中,需要Annotations Support Library的支持

有關自定義proguard-rules.pro文件的更多信息,可以參考ProGuard Manual.這裡Troubleshooting列出了一些常見的問題。

清除無用的資源文件

Resource shrinking 只與Code shrinking 一起工作。在代碼中刪除所有未使用的代碼後,Resource shrinking才可以識別哪些資源的應用程序仍然使用,你必須先刪除未使用的代碼,Resource才會成為無用的,從而被清除掉。

添加shrinkResources true屬性在你的 build.gradle文件中,相應代碼塊如下:

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

resource shrinker 目前還不支持移除定義在values/目錄下的資源文件(strings,dimensions,styles,colors),因為Android Asset Packaging Tool(AAPT)不允許Gradle Plugin指定預定義的版本資源[issue 70869]

指定要忽略的資源文件

如果您希望保留或丟棄特定的資源,請在項目中創建一個XML文件,並使用“資源”標簽,並指定每個資源保存在工具中:保持屬性和每個資源在工具中丟棄:丟棄屬性。兩個屬性都接受一個逗號分隔的資源名稱列表。你可以使用星號作為外卡

相應代碼塊如下:



需要在項目resources目錄保存res/raw/keep.xml文件,build的時候該文件不會被打包到APK裡面。

啟用嚴格的檢測

通常情況下,資源產品可以准確地確定資源使用。如果你使用Resources.getIdentifier()動態獲取指定資源的Id,在默認情況下,這樣資源具有匹配名稱的格式為潛在的使用,無法去除。

例如,下面的代碼將導致所有img_前綴的資源都無法去除。


String name = String.format("img_%1d", angle + 1);
res = getResources().getIdentifier(name, "drawable", getPackageName());

resource shrinker 也通過搜索代碼中是否包含資源名來判斷是否在build的時候刪除。



在 resource 文件中指定 shrinkMode,你可以指定 Gradle 在處理該資源文件時候的方式,默認的值為 safe,你也可以將它指定為 strict(只會保留有明確引用的資源,以及處理被 tools:keep 和 tools:discard 標注的資源)

在後面查看資源回收情況,我們會講到,會遇到有些xml 無法被清除的問題,使用shrinkMode可以解決這個問題。

清除未使用的替代資源

Gradle resource shrinker 只刪除你在代碼中未使用資源,這意味著它不會刪除不同的設備配置的可替代資源。如果有必要,你可以使用Android Gradle plugin 的resconfigs屬性刪除替代資源文件。

例如:我們項目中適配10種國家語言,而項目依賴了v7、v4等其他support包裡面包含20種國家語言,那麼我們可以通過resconfigs 刪除剩余的可替代資源文件,這對於我們APK大小可減少了不少,

以下代碼說明了如何限制你的語言資源,只是英語和法語:

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

像上面那樣通過resconfig屬性指定的語言。未指定的語言的任何資源都被刪除。

同樣的圖片資源我們也可以這麼做,例如:我們提供一套xxhdpi的圖片資源,其他的都過濾清除掉,這樣大量清除了support,其他第三方library的資源文件,關於這個待會我們在後面會說。

合並重復的資源

默認情況下,Gradle也將同名的資源,比如相同的名字,可能是在不同的資源文件夾下,這樣子不能通過shrinkResources屬性來去除。

只有當兩個或多個文件共享相同的資源名稱、類型和限定符時才發生資源合並,關於資源的合並優先級如下:

Gradle 主要從以下位置合並資源:

src/main/res/ 主要資源 Gradle 使用variant(build type 和 build flavors) Library的依賴使用

Gradle合並重復資源優先順序為:

Dependencies → Main → Build flavor → Build type

例如:如果一個重復的資源在你的mian res中和一個Build flavor指定 ,Gradle 會優先選擇Build flavor

查看資源回收

當你Gradle resource shrinker,Gradle Console 輸出日志,移除APK資源的信息。例如:

:android:shrinkDebugResources
Removed unused resources: Binary resource data reduced from 2570KB to 1711KB: Removed 33%
:android:validateDebugSigning

APK構建完成後會Gradle會生成一個resource.txt 在 /build/outputs/mapping/release/ 中,這個文件包括詳細信息,如資源參考其他資源和使用或刪除資源的詳細信息等。

例如:找出為什麼@drawable/ic_plus_anim_016,仍然包含在你的APK中,在resource.txt 搜索該文件名,你可能會發現它是被另一個資源引用,如下:

16:25:48.005 [QUIET] [system.out] @drawable/add_schedule_fab_icon_anim : reachable=true
16:25:48.009 [QUIET] [system.out]     @drawable/ic_plus_anim_016

現在需要知道為什麼@drawable/add_schedule_fab_icon_anim 仍然在使用,搜索我們可以知道應該有代碼引用著add_schedule_fab_icon_anim。

如果你不使用嚴格的檢查(就是上面講的shrinkMode),同樣的我們如果我們在drawable中使用了字符串’#FFFFFF‘ 這樣的使用resource shrinker也不能將他移除在APK中,通常我們可以在Gradle Console中看到以下信息:

10:32:50.590 [QUIET] [system.out] Marking drawable:ic_plus_anim_016:2130837506
    used because it format-string matches string pool constant ic_plus_anim_%1$d.

那麼這種情況我們如何解決呢,可以通過以下方法來清理:

使用tools:discard,在 tools:shrinkMode=”strict” 的時候生效,指定某資源文件需要刪除。在你確定該資源文件無效的時候使用。

利用Lint找出未使用的資源並清理掉

在Android Studio中打開“Analyze” 然後選擇”Inspect Code…”,范圍選擇整個項目,然後點擊”OK”

到這裡APK的大小又小了不少。

使用APK Splits構建APK

雖然我們上面很好的使用了resource shrinker可以回收一些未使用的資源(v7、v4、google Service 等Libarry資源),但有些資源仍然未被清除。

例如:那些未使用的多套替代資源,或者是library內部隱患著引用著的資源而我們卻沒有使用到。或者是我們要根據用戶的手機去提供不同版本的APK,如分辨率(xxhdpi,mhdpi等),so庫等。那麼我們可以使用APK Splits大大的減少一些無用的資源,這裡我們主要參考了http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits 文檔。

APK Splits比起使用 flavors,能讓應用程序更有效地構建一些形式的多個apk。

多 apk 只支持以下類型:

屏幕密度 ABI

使用新的APK Splits,構建同一個應用程序的hdpi版本和mdpi版本,能夠共享很多的任務 (如 javac,dx,proguard)。此外,它會被認為是一個單一的variant,並且同一個測試程序將會被用來測試每??個多APK。

按屏幕密度拆分

android {
  ...
  splits {
    density {
      enable true
      exclude "ldpi", "tvdpi", "xxxhdpi"
      compatibleScreens 'small', 'normal', 'large', 'xlarge'
    }
  }
enable: 啟用屏幕密度拆分機制 exclude: 默認情況下所有屏幕密度都包括在內,你可以移除一些密度。 include: 表示要包括哪些屏幕密度 reset(): 重置屏幕密度列表為只包含一個空字符串 (這能夠實現,在與include一起使用時可以表示使用哪一個屏幕密度,而不是要忽略哪一些屏幕密度) compatibleScreens:表示兼容屏幕的列表。這將會注入到manifest中匹配的 節點。這個設置是可選的。

構建完成後可以在out/apk/目錄下看到多個版本的APK

按 ABI 拆分

android {
  ...
  splits {
    abi {
      enable true
      reset()
      include 'x86', 'armeabi-v7a', 'mips'
      universalApk true
    }
  }
}
enable: 啟用ABI拆分機制 exclude: 默認情況下所有ABI都包括在內,你可以移除一些ABI。 include:指明要包含哪些ABI reset():重置ABI列表為只包含一個空字符串(這可以實現,在與include一起使用來可以表示要使用哪一個ABI,而不是要忽略哪一些ABI) universalApk:指示是否打包一個通用版本(包含所有的ABI)。默認值為 false。

例如:我們項目主要提供xxhdpi的圖片資源,而項目中引用到了很多第三方庫(v7、v4、google Service 等Libarry資源)我們無法使用到,那麼我們可以通過這種方法來去除那些資源。這樣我們的APK又減小了非常多。

使用多版本的APK

Multiple APK Support是一個在Google Play,可以發布不同的應用程序,分別針對不同的設備配置特征。每個APK是一個完整的、獨立的應用程序版本,但他們分享在Google Play相同的應用程序清單,必須共享相同的包名和與簽名。Google Play 會自動給你匹配相應的APK,這樣我們的APK 就可以是分不同版本構建需要資源文件,從而減小APK的大小。

通過發布有多個APK,我們可以:

支持不同OpenGL的APK 支持不同的屏幕尺寸和密度的APK 支持不同的設備功能的APK 支持不同的平台版本的APK

支持不同的CPU架構,每個apk(如ARM、x86,MIPS等)的APK

更多相關信息請參考https://developer.android.com/google/play/publishing/multiple-apks.html

目前我們基於這個方案做了不同屏幕的APK。

資源動態加載

我們可以在項目中使用資源動態加載形式,例如:表情,語言,離線庫等資源動態加載,減小APK的大小。

依賴庫優化

如果應用支持的最低版本是API14,那就不要使用android support庫,或者分開使用android support庫,用哪個引入哪個庫(android-support-ui/android-support-core 等)雖然現在支持還不太好,Google servie 也是如此。 使用更輕量級的庫代替,或者優化library的大小,不然自己寫更好。 刪除armable-v7包下的so、刪除x86包下的so,基本上armable的so也是兼容armable-v7的,armable-v7a的庫會對圖形渲染方面有很大的改進,不過最好的是根據上面我們說的提供多版本APK,對不同的平台精簡,再或者動態的加載so。 使用H5編寫界面,圖片雲端獲取 資源緩存庫不放在assets下,雲端獲取更新。

支持插件化

未來對於一些獨立業務模塊,可以做成插件化動態加載,用戶需要使用時,只需下載少部分插件。

使用ReDex優化

ReDex是Facebook開源一個減小安卓app大小以提高性能的工具,內嵌以及清除僵屍代碼這樣的優化來減小字節碼,主要是對Dex進行了優化,能讓APK 運行更快,不過需要多測試是否會崩潰。

教程很簡單具體更詳細的內容請參考:
https://code.facebook.com/posts/998080480282805/open-sourcing-redex-making-android-apps-smaller-and-faster/

github地址:
https://github.com/facebook/redex.git

 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved