性能優化7--App瘦身

来源:https://www.cnblogs.com/ganchuanpu/archive/2018/07/08/9280846.html
-Advertisement-
Play Games

1、 前言 如果你對App優化比較敏感,那麼Apk安裝包的大小就一定不會忽視。關於瘦身的原因,大概有以下幾個方面: 對於用戶來說,在功能差別不大的前提下,更小的Apk大小意味更少的流量消耗,也意味著更多的用戶下載; 對於產品來說,大於競品的Apk意味著較低的下載基數,不利於驗證產品策略; 對於開發人 ...


1、 前言

如果你對App優化比較敏感,那麼Apk安裝包的大小就一定不會忽視。關於瘦身的原因,大概有以下幾個方面:

  • 對於用戶來說,在功能差別不大的前提下,更小的Apk大小意味更少的流量消耗,也意味著更多的用戶下載;
  • 對於產品來說,大於競品的Apk意味著較低的下載基數,不利於驗證產品策略;
  • 對於開發人員來說,App瘦身則是一次技術優化、技術提升的機會;

2、 Apk的組成

2.1 Apk典型組成

  一個典型的Apk組成

下表為Apk目錄及文件說明:

文件/目錄說明
assets/ 存放一些靜態文件,可以通過AssertManager訪問
lib/ 如果該目錄存在,一般存放的是NDK編譯出來的so
META-INF/ 保存著APK的簽名信息
res/ 資源文件所在目錄,包含drawable、layout等
AndroidManifest.xml 程式全局配置文件
classes.dex Java Class,被DEX編譯後可供Dalvik/ART虛擬機所理解的文件格式
resources.arsc 編譯後生成的二進位資源文件

2.2 示例分析

做App瘦身之前需要對自己App現有組成有一個清晰的認識,上述解壓的方式只能粗略的看出具體目錄的大小,但是有用信息仍然有限。

2.2.1 Android Studio Analyze APK

Android Studio 2.2之後有一個功能Analyze APK,方便簡單,功能還是Google自帶的靠譜;

  • 查看apk中任意文件的大小,得到一個直觀的認識;
  • 瞭解Dex文件的組成,查看使用那些開源庫等;
  • 查看二進位文件(如AndroidMainfest.xml等);
  • Apk的比較,便於發現兩個版本之間的區別。
  Analyze APK的使用
2.2.2 反編譯工具ClassyShark

ClassShark 是一款查看Android執行文件(apk)的瀏覽工具,可以很方便的打開APK/Class/Jar/res等文件和分析裡面的內容。

  ClassyShark的使用
2.2.3 Nimbledroid

NimbleDroid 是美國哥倫比亞大學的博士創業團隊研發出來的分析Android app性能指標的系統,分析的方式有靜態和動態兩種方式,其中靜態分析可以分析出APK安裝包中大文件排行榜,各種知名SDK的大小以及占代碼整體的比例,各種類型文件的大小以及占排行,各種知名SDK的方法數以及占所有dex中方法數的比例。

  文件大小排行
  方法數統計

總結:這三種方式都可以對Apk的組成有一個更加清晰的認識,但更加推薦使用AndroidStudio自帶的Analyze APK,簡單、高效。

使用Analyze APK查看到文件大小之後發現,classes.dex、res、assets、lib等文件較大,哪裡的脂肪多,我們就去抽哪裡。確定優化方向:

  1. 代碼部分:冗餘代碼、無用功能、代碼混淆、方法數縮減等;
  2. 資源部分:冗餘資源、資源混淆、圖片處理等;
  3. 對So文件的處理等。

3、 Apk瘦身之代碼瘦身

3.1 移除無用代碼、功能

隨著版本的迭代,部分功能可能已被去掉,但是其代碼還存在項目中。移除無用代碼以及無用功能,有助於減少代碼量,直接體現就是Dex的體積會變小。

備註:根據經驗,不用的代碼在項目中存在屬於一個普遍現象,相當於僵屍代碼,而且這類代碼過多也會導致Dex文件過大。

3.2 移除無用的庫、避免功能雷同的庫

3.2.1 項目中基礎功能的庫要統一實現,避免出現多套網路請求、圖片載入器等實現。
3.2.2 不用的庫要及時移除出項目,例如我們之前確定要由某推送切換到某推送的時候,此時就要把最初項目中的推送庫去掉,而不應該只是註釋掉其註冊代碼。
3.2.3 一些功能可以曲線救國的話就不要引入SDK,例如定位功能,可以不引入定位SDK,而通過拿到經緯度然後調用相關介面來實現;同樣實現了功能而沒有引入SDK。
3.2.4 而引入SDK也需要考慮其方法數,可以使用ClassyShark、Nimbledroid或者APK method count等工具查看。

備註:根據經驗,項目中存在之前使用之後不使用的庫的情況並不罕見。

3.3 啟用Proguard

代碼混淆也稱為花指令,是將電腦程式的代碼轉換為功能上等價但是難以閱讀、理解的行為。Proguard是一個免費的Java類文件壓縮、優化、混淆、預先驗證的工具,可以檢測和移除未使用的類、欄位、方法、屬性,優化位元組碼並移除未使用的指令,並將代碼中的類、欄位、方法的名字改為簡短、無意義的名字。

可以看出Proguard不僅能將diamante中的各種元素改的簡短,還可以移除冗餘代碼,因此可以減少Dex文件的大小。

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

其中,proguard-android.txt是獲取預設ProGuard設置,proguard-rules.pro文件用於添加自定義ProGuard規則。

備註:對於Proguard,雖然效果很明顯,但仍然需要謹慎;

  • 代碼混淆會拖慢項目構建速度,因此debug模式下關掉Proguard不至於RD在運行代碼的時候抓狂;
  • 因為在debug模式下關掉了Proguard,如果混淆規則沒有配置好,在Release模式可能會出現debug模式下不出現的Bug;
  • Proguard也不是你想搞就能搞,如果App做了一段時間之後再來做Proguard,項目包結構不規範的話,那Proguard的規則將會非常多。而短時間內調整包結構也是一件相當痛苦的事情。

3.4 縮減方法數

一般情況下縮減方法數,都是為了Android著名的64k方法數問題,此處不再回顧,參見之前《關於Multidex的系列文章》。而這裡說縮減方法數的目的,是為了App瘦身。

通過《Dalvik Executable format》,我們可以看到Dex文件的組成。而從header-item表中的method-ids-size欄位可以看出,方法數縮減之後,可以減少方法列表的大小;同時,方法在Dex文件中的占用空間也減少了,App自然被瘦身。

而縮減方法數,除了上面寫到的普遍方法:移除無用方法、庫、使用較小的SDK之外還有:

  1. 避免在內部類中訪問外部類的私有方法、變數。擋在Java內部類(包含匿名內部類)中訪問外部類的私有方法、變數的時候,編譯器會生成額外的方法,會增加方法數;
  2. 避免調用派生類中的未被覆寫的方法,避免在派生類中調用未覆寫的基類的方法;避免用派生類的對象調用派生類中未覆蓋的基類的方法。調用派生類中的未被覆蓋的方法時,會多產生一個方法數;
  3. 去掉部分類的get、set方法;當然這樣會犧牲一些面向對象的觀念。

4、 Apk瘦身之資源瘦身

對於重要性,代碼和資源的瘦身同樣重要,但是從效果上來說,資源文件的瘦身效果比代碼的瘦身效果要好非常多。很有可能費力許久在代碼上得到的瘦身效果,在資源文件瘦身中輕鬆就得到了。

4.1 移除無用的資源文件

移除無用資源文件要比移除無用代碼容易,在Android Studio的任何文件中右擊,選擇清除無用資源即可刪除沒有用到的資源文件。

  Remove Unused Resources

備註:在build.gradle中設置shrinkResources為true後,每次打包的時候就會自動排除無用的資源。shrinkResources需要配合minifyEnabled一起使用。但是根據我的實驗:無用的資源還是會被打進Apk中,只是變成一張黑圖,體積也非常小,只有不到100b。有使用錯誤的地方歡迎指正!

4.2 Drawable目錄只保留一份資源

這條開發者中討論的比較多,確實Google強烈建議根據不同屏幕密度準備多套切圖資源來做適配的。但是鑒於Android上對UI要求不會是最頂級的那種高度,以及即便是放在合適(註意這兩個字)一個的目錄下,在不同的解析度下也會做自動的適配(等比例拉伸、縮放);因此還是建議:對UI不是最頂級要求的話根據自己的用戶群體機型放在一個合適的目錄下。這樣毋庸置疑可以縮減Res的大小,進而減少Apk的體積。

備註:圖片放在不恰當的目錄有可能會對記憶體產生較大的影響,可以參考之前的文章《Android 性能優化(五)之細說 Bitmap》

4.3 對圖片進行壓縮

之前我在項目里發現過文件大小過1M的圖片,可能是由於UI同學和RD同學的雙重疏忽,導致如此大的圖片到了項目中,對Apk體積的影響自然不言而喻。

可以考慮使用TinyPng、pngquant、ImageOptim等工具對圖片進行壓縮,這些工具可以減少PNG文件大小,同時保持圖像質量。

此處以TinyPng為例:TinyPng是一個相當不錯的圖片壓縮工具,在保持alpha通道的情況下對PNG的壓縮可以達到1/3之內,而且用肉眼基本上分辨不出壓縮的損失。這張3.4M的圖片被壓縮到了984.7k,壓縮率高達71%。

  使用TinyPng壓縮圖片示例

 

也有同學開發了一個AndroidStudio插件:TinyPngPlugin,能夠批量地壓縮項目中的圖片,更加方便。

備註:需要註意的是在Android構建流程中AAPT會使用內置的壓縮演算法來優化res/drawable/目錄下的PNG圖片,但也可能會導致本來已經優化過的圖片體積變大,可以通過在build.gradle中設置cruncherEnabled來禁止AAPT採用預設方式優化我們已經優化過的圖片。

aaptOptions {
    cruncherEnabled = false
}

4.4 PNG轉換JPG

PNG是一種無損格式,JPG是有損格式。JPG在處理顏色很多的圖片時,根據壓縮率的不同,有時會去掉一些肉眼識別差距較小的中間顏色。但是PNG對於無損這個基本要求,會嚴格保留所有的色彩數。所以圖片尺寸大,或者色彩數量多特別是漸變色的多的時候,PNG的體積會明顯大於JPG。

在這種情況下,我們可以有所取捨。小尺寸、色彩較少或者有alpha通道透明度的時候,使用PNG;大尺寸、色彩漸變多的使用JPG。

備註:根據經驗,對於可以直接使用JPG格式的圖片,最好不要從PNG轉換為JPG,而是出圖的時候直接出JPG格式的圖片,相對而言,後者的效果更好。

4.5 使用矢量圖

可縮放矢量圖形(英語:Scalable Vector Graphics,SVG)是一種基於可擴展標記語言(XML),用於描述二維矢量圖形的圖形格式。SVG由W3C制定,是一個開放標準。可以使用矢量圖形來創建獨立於解析度的圖標和其他可伸縮圖片。使用矢量圖片能夠有效的減少App中圖片所占用的大小,矢量圖形在Android中表示為VectorDrawable對象。

優點

  • 圖片擴展性:不損傷圖片質量,一套圖適配所有;
  • 圖片非常小:比使用點陣圖小十幾倍,有利於減小apk體積;

缺點

  • 性能優損失,系統渲染VectorDrawable需要花費更多時間,因為矢量圖的初始化載入會比相應的光柵圖片消耗更多的CPU周期,但是兩者之間的記憶體消耗和性能接近;
  • 矢量圖主要用在色調單一的icon。

4.6 使用WebP

Google於2010年提出了一種新的圖片壓縮格式 — WebP,為圖片提供了無損和有損壓縮能力,同時在有損條件下支持透明通道。據官方實驗顯示:無損WebP相比PNG減少26%大小;有損WebP在相同的SSIM(Structural Similarity Index,結構相似性)下相比JPEG減少25%~34%的大小;有損WebP也支持透明通道,大小通常約為對應PNG的1/3。同時,谷歌於2014年提出了動態WebP,拓展WebP使其支持動圖能力。動態WebP相比GIF支持更豐富的色彩,並且也占用更小空間,更適應移動網路的動圖播放。

優點:

  • WebP在同畫質下體積更小,WebP支持透明度,壓縮比比JPEG更高但顯示效果卻不輸於JPEG;
  • 可以通過工具、雲服務等進行PNG到WebP的轉換;

缺點:

  • Android從4.0才開始WebP的原生支持,意味著要相容4.0以下機型需要添加適配庫;當然現在市面上適配4.0以下的應用已經很少了。
  • Android 4.2.1+才支持顯示含透明度的WebP,因此最低版本小於4.2.1的App也不是想用就能用的。可以將不顯示透明度的圖片轉換為WebP。

4.7 資源混淆

在Apk打包過程中,aapt會將每一個資源生成一個對應的int數值,而我們通過這個int值來查找使用資源。在Apk構成中,我們可以看到裡面有一個resources.arsc文件,裡面保存著資源id和資源key的映射關係。

當調用圖片時,先找到drawable分類,再根據當前的系統config找到匹配的config表,根據id找到對應的res數據。drawable在arsc中是當做string類型保存的,res數據中有這個資源在res string pool池中的索引。根據這個索引可以在字元串池中找到一個字元串。這個字元串其實就是一個路徑,比如:res/drawable-xhdpi/icon.png;混淆就是將這個路徑改為R/s/f.png;同時修改resources.arsc文件的映射關係。這樣就能清楚的看出來資源混淆能減小Apk的原因:

  • resources.arsc變小;
  • 文件信息變小,採用了超短路徑,res/drawable-xhdpi/icon.png被修改為R/s/f.png。

這裡推薦微信的資源混淆方案:AndResGuard

4.8 資源線上化

將部分使用頻率不高的資源例如圖片,放在網上,在恰當的時機提前下載,這樣也能節約部分空間。

5、 Apk瘦身之So瘦身

So(shared object,共用庫)是機器可以直接運行的二進位代碼,是Android上的動態鏈接庫,類似於Windows上的dll。每一個Android應用所支持的ABI是由其APK提供的.so文件決定的,這些so文件被打包在apk文件的lib/目錄下。

So的常見的場景如:加解密演算法、音視頻編解碼、核心代碼等。在生成SO文件時,需要考慮適配市面上不同手機CPU架構,而生成支持不同平臺的SO文件進行相容。目前Android共支持七種不同類型的CPU架構,分別是:ARMv5,ARMv7 (從2010年起),x86 (從2011年起),Mips (從2012年起),ARMv8,Mips64和x86_64 (從2014年起)。

理論上對應CPU架構的So的執行效率是最高的,但是這樣會導致在libs目錄下放置各個架構平臺的So文件,Apk文件的大小自然也就更大了。那麼我們自然想到縮減Libs的目錄,一般情況(註意限定)下留下armeabi目錄即可,armeabi目錄下的So可以相容別的平臺的So,但是性能會有所損耗,失去對特定平臺的優化。

因此需要根據自己使用到的So功能來做具體的區分:對於性能敏感模塊使用的So可以都放在armeabi目錄,然後通過代碼判斷設備的CPU類型,再載入其對應架構的SO文件,例如微信就是這麼做的。既縮減了Apk的體積,也不影響性能敏感模塊的執行。

  微信So的使用

移除特定平臺So的方式,這樣打包就只保存armeabi里的So。

ndk {
    //設置支持的SO庫架構
    abiFilters  'armeabi'
}

備註:原本x86架構的CPU是不支持運行arm架構的So,但Intel和Google合作在x86機子的系統內核層之上加入了一個名為houdini的Binary Translator(二進位轉換中間層),這個中間層會在運行期間動態的讀取arm指令並將之轉換為x86指令去執行。

6、 Apk瘦身之7Zip壓縮

我們知道Apk文件實際上就是一個Zip文件。Android SDK的打包工具apkbuilder採用的是Deflate演算法將Android App的代碼、資源等文件進行壓縮,壓縮成Zip格式,然後簽名發佈。

既然是壓縮,那能不能改進其壓縮方式,獲取更小的Apk文件?通過分析Apk打包的流程圖我們可以發現SignedJarBuilder類對整個工程包括代碼Dex和一些課壓縮的資源、文件進行壓縮,使用的是JDK中zip包下提供的演算法。

  使用7Zip壓縮

簡單的方式我們可以在不改變App編譯器工作的情況下,對生成的Apk文件進行二次壓縮,同樣使用Deflate演算法,但是將壓縮等級從標準提升到極限壓縮。提高壓縮級別可在不對Apk包本身的內容做任何修改的情況下得到更小的Apk。

備註:

  • 需要註意這樣極限壓縮之後的簽名被破壞,需要重新簽名。
  • Android平臺對Apk安裝包的解壓演算法只支持Deflate演算法,其它演算法如LZMA,雖然壓縮率更好,但是由於Android平臺預設不支持,所以如果採用這種演算法壓縮Apk,會導致Apk無法安裝。
  • 目前在Mac上沒發現好用的7Zip壓縮軟體,需要在Windows下使用。

7、 App瘦身總結:

7.1 代碼瘦身

  • 移除無用代碼、功能;
  • 移除無用的庫、避免功能雷同的庫;
  • 啟用Proguard;
  • 縮減方法數;

7.2 資源瘦身

  • 移除無用的資源文件;
  • Drawable目錄只保留一份資源;
  • 對圖片進行壓縮;
  • PNG轉換JPG;
  • 使用矢量圖;
  • 使用WebP;
  • 資源混淆;
  • 資源線上化;

7.3 So瘦身

  • 在允許的情況下,針對用戶機型分佈保留特定架構的So;

7.4 7Zip壓縮

使用7Zip對Apk進行極限壓縮。

7.5 其它

  • 類如插件化,將Dex與資源文件放在服務端,需要時下載;但是插件化實施以及與現有項目結合難度不小,也超出本文主題,不細說;
  • 通過在 build.gradle配置include來針對每個CPU架構生成單獨的安裝包,按照架構上傳Apk;但是這個方案在國內應用市場幾乎沒有採用的,只能在Google Play上使用。
  • 一點經驗:對Apk進行瘦身,瘦身So以及資源文件是見效最快的操作。瘦身So以及刪除不用的圖片、壓縮圖片之後,Apk會縮減很大的比例;而針對Dex的優化可能作用不會很明顯。

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

-Advertisement-
Play Games
更多相關文章
  • 1.簡介和安裝 sysbench是一個很不錯的資料庫性能測試工具。 官方站點:https://github.com/akopytov/sysbench/ rpm包下載:https://packagecloud.io/akopytov/sysbench/packages/el/7/sysbench 1 ...
  • 1.簡介 1.1.sql:Structured Query Language 結構化查詢語言 1.2.windows在目錄路徑中使用反斜線\,unix和linux使用正斜線/ 1.3.Number(a,b) a為總有效位數,b為最多小數位數 1.4.Insert into 表名(需指定主鍵及要求非空 ...
  • 視頻課程:李興華 Oracle從入門到精通視頻課程 學習者:陽光羅諾 視頻來源:51CTO學院 Oracle資料庫從入門到精通-單行函數 在資料庫中,為了方便用戶的數據開發,往往會提供一系列的支持函數,利用這些函數可以針對於數據處理。 例如:在進行根據姓名查詢的時候,如果說姓名本身是大寫字母,而查詢 ...
  • 提示框,菜單,數據存儲,組件篇 Toast Toast.makeText(context, text, 時間).show(); setDuration();//設置時間 setGravity();//位置 獲取: 添加toast.getView(); imageView添加到toast中,addVi ...
  • 前言 大家好,給大家帶來 的概述,希望你們喜歡 Activity是什麼 作為一個Activity,就是一個界面,當我們在手機上打開一個APP時,你看到的頁面就是基於Activity生成的。 那麼你再點擊一個按鈕跳轉到另一個界面時,就是又一個Activity界面,由Activity可以分出很多的知識點 ...
  • 前言 大家好,給大家帶來 的概述,希望你們喜歡 TextView控制項 TextView控制項有哪些屬性: EditText控制項 EditText控制項有哪些屬性: 設置顏色 1. 在xml中是android:textColor 2. 在Activity中是setTextColor AutoComplet ...
  • 前言 大家好,給大家帶來 的概述,希望你們喜歡 內容 什麼是Sqlite: 效率高,開源,小型,程式驅動,支持事務操作,無數據類型,可嵌入的關係型資料庫 獨立的,跨平臺的,代碼量少,簡單易用 創建表語句 刪除表 插入數據 修改數據 更新數據 刪除數據 查詢語句 內容 創建資料庫 實現資料庫中的增刪改 ...
  • git上clone一個Gradle項目,使用AS的gradle sync報錯如下: 原因:本地gradle版本不匹配。 解決方法:修改根目錄下的build.gradle 中的classpath為本地已經下載的gradle版本或者本地下載安裝依賴版本。 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...