縮減代碼和資源(Shrink Your Code and Resources)

一、代碼縮減和資源縮減概述

為盡可能縮減apk包的大小,我們應該在release版本中移除未使用的代碼和資源。這篇文檔描述如何在構建過程中指定保留和移除的代碼與資源。

代碼縮減(Code shrinking)利用 ProGuard ,它可以檢測和移除app中沒有使用的類、字段、方法和屬性,包括來自代碼庫的那些。ProGuard還可以優化class文件,刪除未使用的代碼指令,并使用短名稱來混淆類字段和方法。

資源縮減(Resource shrinking)可利用Gradle配置,它可以移除app中未使用的資源,包括代碼庫中未使用的資源。它與代碼縮減一起工作,使得一旦未使用的代碼被移除,任何不再被引用的資源也可以被安全地移除。

二、代碼縮減(Shrink Your Code)

1.啟用代碼縮減

要使用ProGuard啟用代碼縮減,請將 minifyEnabled true 添加到build.gradle文件中的相應build type。

注意,代碼縮減會減慢構建時間,因此,盡量避免在dbeug版本上使用它。不過重要的是,必須在在啟用代碼縮減的apk上進行測試,因為如果沒有足夠自定義要保留的代碼,它可能會引入錯誤。

代碼縮減gradle配置示例:

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

除了指定minifyEnabled屬性,proguardFiles屬性還定義了ProGuard規則:

  • getDefaultProguardFile('proguard-android.txt') 從SDK tools /proguard /下的proguard-android.txt設置默認的ProGuard設置。
    (備注:為更好地代碼縮減,可以利用位于相同位置的 proguard-android-optimize.txt 文件。 它包括相同的ProGuard規則,但在執行分析字節碼級別等方面進一步優化,可以更好地減少apk大小,并幫助它運行更快。)
  • proguard-rules.pro 中可以添加自定義ProGuard規則。默認情況下,此文件位于module的根目錄(在build.gradle文件旁邊)。

我們也可以為不同的Variant指定不同的ProGuard規則:

android {
...
  buildTypes {
    release {
      minifyEnabled true
    proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
    }
  }
  productFlavors {
    flavor1 {
    }
    flavor2 {
      proguardFile 'flavor2-rules.pro'
    }
  }
}

項目構建后,在/build/outputs/mapping/release/下會生成幾個文件:

  • dump.txt:apk文件中所有類文件間的內部結構
  • mapping.txt:混淆前后代碼間的映射
  • seeds.txt:未被混淆的類和成員
  • usage.txt:未使用的、被apk刪除的代碼

2.定義保留哪些代碼

大多數情況下,默認的ProGuard配置文件(proguard-android.txt)就足夠了,ProGuard刪除所有未使用的代碼。然而,有些情況下,ProGuard很難正確分析,它可能會刪除您的應用程序實際需要的代碼。比如:

  • 當僅在AndroidManifest.xml文件中引用的類
  • 當從JNI調用方法時
  • 當在字節碼中操作代碼時(如使用反射)
    為修復錯誤并強制ProGuard保留某些代碼,需要在ProGuard配置文件中添加一個 -keep 標記,比如:

    -keep public class MyClass

或者,您可以將 @Keep 注釋添加到要保留的代碼。請注意,此注釋僅在使用注釋支持庫時可用。

使用-keep選項時,您應該考慮許多因素;

3.解析混淆后的堆棧跟蹤(Stack trace)

ProGuard縮減代碼后,讀取堆棧跟蹤是很困難的,因為方法名都被混淆處理了。幸運的是,ProGuard在構建過程中創建了mapping.txt作為混淆前后代碼的映射。

由于mapping.txt在每次構建過程中會被重寫,所以需要在每個版本對應的mapping.txt保存到對應的目錄下。如果用戶從舊版本app提交混淆的堆棧跟蹤,我們就可以通過每個版本對應的mapping.txt,來調試問題。

當我們在應用商店發布app時,也可以將對應的mapping.txt提交上去,這樣應用商店就可以將用戶報告的問題中直接進行解析。

要將混淆的堆棧跟蹤轉換為可讀的堆棧跟蹤,請使用回溯腳本(Windows上為 retrace.bat ;Mac上為retrace.sh)。 它位于 / tools / proguard /目錄中。該腳本采用mapping.txt文件和堆棧跟蹤,產生一個新的、可讀的堆棧跟蹤。

使用retrace.bat的語法:

retrace.bat|retrace.sh [-verbose] mapping.txt []

例如:

retrace.bat -verbose mapping.txt obfuscated_trace.txt

三、資源縮減(Shrink Your Resources)

1.啟用資源縮減

資源縮減必須與代碼縮減相結合。代碼縮減移除所有未使用的代碼后,資源縮減器可以識別應用程序仍在使用哪些資源。 未被使用的資源,將會被資源縮減器移除。

為使用資源縮減,需要在gradle文件中設置 shrinkResources 屬性,例如:

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

注意,資源縮減不會移除value文件下的資源(比如strings, dimensions, styles, and colors),aapt不允許gradle這樣做。

2.指定保留哪些資源

對于一些你希望保留或移除的資源,可以創建一個xml文件,以resource為標簽,通過 tools:keeptools:discard 指定要保留和要移除的資源,多個資源之間以逗號分隔。例如:

tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
tools:discard="@layout/unused2" />

將此文件保存在項目資源中,比如res/raw/keep.xml,構建時不會將該文件打包到apk中。

指定要移除的資源,比起來直接刪除掉,似乎有些傻。但是,對于構建多個Variant,卻是很有用的。我們可以為不同的變體指定不同的keep.xml。

3.啟用嚴格引用檢查

通常,資源縮減器能夠判斷出指定資源是否被使用。但是,如果你的代碼調用了Resources.getIdentifier()(或者你的庫中的任何一個,比如AppCompat庫),這意味著你的代碼是基于動態生成的字符串查找資源名稱。這樣操作時,資源縮小器默認情況下會防御性地運行,并將匹配名稱格式的所有資源標記為可能已使用而不會去移除。

例如,以下代碼會將所有帶有img_前綴的資源標記為已使用。

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

資源縮小器還查看代碼中的所有字符串常量以及各種res / raw /資源,以類似于file:///android_res/drawable//ic_plus_anim_016.png的格式查找資源url。如果它發現類似這樣的字符串或其他,也不會移除它們。

這些是默認情況下啟用的安全縮減模式的示例。但是,當不關注#“better safe than sorry”#時,可以指定資源縮減器僅保留其確定使用的資源。為此,請在keep.xml文件中將shrinkMode設置為strict,如下所示:

tools:shrinkMode="strict" />

如果您啟用了嚴格縮減模式,并且您的代碼還引用了具有動態生成的字符串的資源,那么您必須使通過tools:keep手動保留這些資源。

4.移除帶有選擇性的資源

資源縮減器只會移除沒有在代碼中被引用的資源,這也意味著,它不會移除那些為不同設備配置的帶有選擇性質的資源。必要的情況下,可以利用 resConfigs 屬性移除這些資源。

例如,如果您使用的庫包含語言資源,則apk會包含這些庫中的所有翻譯語言字符串。如果您只想保留指定的語言,可以使用resConfig屬性進行指定,并將刪除未指定語言的任何資源。

以下代碼段顯示了如何將語言資源僅限英語和法語:

android {
  defaultConfig {
  ... ...
    resConfigs“en”,“fr”
  }
}

同樣,您可以自定義要在apk中包含哪些屏幕密度或ABI資源,并為不同設備構建不同的apk。

5.合并重復的資源

這里的合并英文為 merge ,帶有解決沖突的意思。

默認情況下,gradle會合并相同名稱的資源,例如可能在不同資源文件夾中具有相同名稱的drawable。 此行為不受shrinkResources屬性控制,不能禁用,因為必須避免在多個資源與代碼查找的名稱匹配時出現錯誤。

僅當兩個或多個文件共享相同的資源名稱時,才會進行資源合并。gradle選擇哪個文件被認為是重復項中的最佳選擇(基于下面描述的優先級順序),并且僅將那個資源傳遞給aapt以在apk中分發。

gradle在以下位置查找重復的資源:

  • main resources,與主源集相關,一般位于src / main / res /。
  • 變體覆蓋,從the build type和build flavors。
  • 庫項目依賴項。
    gradle在以下優先級順序中合并重復資源:

    Dependencies→Main→Build flavor→Build type

例如,如果重復資源出現在main resources和build flavor,gradle會選擇build flavor中的。

6.資源合并描述文件

當縮減資源時,Gradle Console會顯示從應用程序包中刪除的資源的摘要。例如:

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

gradle還會在/build/outputs/mapping/release/創建一個文件 resources.txt ,來描述哪些資源被引用和被移除。

 

來自:http://www.jianshu.com/p/46560895b42c

 

 本文由用戶 LaurelBosto 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!