ProGuard
Xamarin.Android ProGuard 是 Java 類別檔案壓縮工具、最佳化工具和預先驗證器。 它可以偵測和移除未使用的程式碼,分析位元組程式碼並予以最佳化。 本指南說明 ProGuard 的運作方式、如何在專案中予以啟用,以及如何加以設定。 文中也提供數個 ProGuard 組態範例。
概觀
ProGuard 可偵測和移除封裝應用程式的未使用類別、欄位、方法及屬性。 即使對參考的程式庫也能這樣做 (這有助於避免 64k 參考限制)。 Android SDK 提供的 ProGuard 工具也會將位元組程式碼最佳化,以及移除未使用的程式碼指令。 ProGuard 會讀取輸入 jar,然後予以壓縮、最佳化並預先驗證;它會將結果寫入一或多個輸出 jar。
ProGuard 會使用下列步驟來處理 APK 的輸入:
壓縮步驟 – ProGuard 遞歸決定要使用哪些類別和類別成員。 所有其他類別與類別成員都會被捨棄。
優化步驟 – ProGuard 會進一步優化程序代碼。 在其他最佳化之中,並非進入點的類別與方法可設為私人、靜態或最終的進入點,可以移除未使用的參數,而且可以內嵌某些方法。
混淆步驟 – 在原生 Android 開發中,ProGuard 會重新命名不是進入點的類別和類別成員。 保留進入點可確保仍可透過其原始名稱來存取。 但是 Xamarin.Android 並不支援這項步驟,原因是該應用程式以中繼語言 (IL) 編譯而成。
Preverification 步驟 – 在運行時間之前對 Java 位元組程式代碼執行檢查,併為 Java VM 的優點標註類別檔案。 這是唯一不需要知道進入點的步驟。
這些全都是「選擇性」步驟。 如下一節中所說明,Xamarin.Android ProGuard 只會使用這些步驟的某些步驟。
Xamarin.Android 中的 ProGuard
Xamarin.Android ProGuard 組態不會混淆 APK。 事實上,您無法透過 ProGuard 啟用混淆功能 (即使是使用自訂設定檔)。 因此,Xamarin.Android 的 ProGuard 只會執行壓縮和最佳化步驟:
使用 ProGuard 之前要事先知道的一個重要項目就是它在 Xamarin.Android
建置處理序中的運作方式。 此程序會使用兩個不同的步驟:
Xamarin Android 連結器
ProGuard
所有這些步驟都會描述如下。
連結器步驟
Xamarin.Android 連結器會為您的應用程式使用靜態分析,以判斷下列各項:
實際上會使用哪些組件。
實際上會使用哪些類型。
實際上會使用哪些成員。
連結器一律會在 ProGuard 步驟之前執行。 因此,連結器能移除您可能預期 ProGuard 要在其上執行的組件/類型/成員 (如需 Xamarin.Android 中之連結的詳細資訊,請參閱 Android 上的連結)。
ProGuard 步驟
成功完成連結器步驟之後,ProGuard 會執行以移除未使用的 Java 位元組程式碼。 這是將 APK 最佳化的步驟。
使用 ProGuard
若要在應用程式專案中使用 ProGuard,您必須先啟用 ProGuard。 接下來,您可以讓 Xamarin.Android 建置處理序使用預設 ProGuard 組態檔,或您可以建立自己的組態檔以供 ProGuard 使用。
啟用 ProGuard
使用下列步驟以在您的應用程式專案中啟用 ProGuard:
請務必將專案設定為 [發行] 組態 (這相當重要,因為連結器必須執行,ProGuard 才能執行):
從 [屬性 > Android 選項] 視窗上的 [程式代碼壓縮器] 下拉式清單中選擇 ProGuard:
針對大部分的 Xamarin.Android 應用程式而言,Xamarin.Android 提供的預設 ProGuard 組態檔即足以移除所有 (且僅限) 未使用的程式碼。 若要檢視預設的 ProGuard 組態,請在 obj\Release\proguard\proguard_xamarin.cfg 開啟檔案。
下列範例示範一般產生的 proguard_xamarin.cfg 檔案:
# This is Xamarin-specific (and enhanced) configuration.
-dontobfuscate
-keep class mono.MonoRuntimeProvider { *; <init>(...); }
-keep class mono.MonoPackageManager { *; <init>(...); }
-keep class mono.MonoPackageManager_Resources { *; <init>(...); }
-keep class mono.android.** { *; <init>(...); }
-keep class mono.java.** { *; <init>(...); }
-keep class mono.javax.** { *; <init>(...); }
-keep class opentk.platform.android.AndroidGameView { *; <init>(...); }
-keep class opentk.GameViewBase { *; <init>(...); }
-keep class opentk_1_0.platform.android.AndroidGameView { *; <init>(...); }
-keep class opentk_1_0.GameViewBase { *; <init>(...); }
-keep class android.runtime.** { <init>(***); }
-keep class assembly_mono_android.android.runtime.** { <init>(***); }
# hash for android.runtime and assembly_mono_android.android.runtime.
-keep class md52ce486a14f4bcd95899665e9d932190b.** { *; <init>(...); }
-keepclassmembers class md52ce486a14f4bcd95899665e9d932190b.** { *; <init>(...); }
# Android's template misses fluent setters...
-keepclassmembers class * extends android.view.View {
*** set*(***);
}
# also misses those inflated custom layout stuff from xml...
-keepclassmembers class * extends android.view.View {
<init>(android.content.Context,android.util.AttributeSet);
<init>(android.content.Context,android.util.AttributeSet,int);
}
下一節說明如何建立自訂的 ProGuard 組態檔。
自訂 ProGuard
您可以選擇新增自訂的 ProGuard 組態檔以對 ProGuard 工具施加更多控制。 例如,您可能想要明確告訴 ProGuard 要保留哪些類別。 若要執行此動作,請建立新的 .cfg 檔案,並在 [方案總管] 的 [屬性] 窗格中套用 ProGuardConfiguration
建置動作:
請記住,由於 ProGuard 會使用兩者,所以此組態檔不會取代 Xamarin.Android proguard_xamarin.cfg 檔案。
有些情況可能是 ProGuard 無法適當分析應用程式;它可能會移除應用程式真正需要的程式碼。 如果發生這種情況,您可以將 -keep
這行加入自訂 ProGuard 組態檔:
-keep public class MyClass
在此範例中,MyClass
設定為您想要 ProGuard 略過的實際類別名稱。
您也可以使用 [Register]
註釋來登錄自己的名稱並使用這些名稱來自訂 ProGuard 規則。 您可以登錄 Adapters、Views、BroadcastReceivers、Services、ContentProviders、Activities 及 Fragments 的名稱。 如需使用 [Register]
自訂屬性的詳細資訊,請參閱使用 JNI。
ProGuard 選項
ProGuard 提供一些您可以設定的選項,以對其作業進行更細微的控制。 ProGuard 手冊 \(英文\) 提供使用 ProGuard 的完整參考文件。
Xamarin.Android 支援下列 ProGuard 選項:
Xamarin.Android 會乎略下列選項:
ProGuard 和 Android Nougat
如果您嘗試使用 ProGuard Android 7.0 或更新版本,您必須下載較新版的 ProGuard,因為 Android SDK 並未隨附與 JDK 1.8 相容的新版本。
您可以使用此 NuGet 套件來安裝較新版的 proguard.jar
。
如需更新預設 Android SDK proguard.jar
的詳細資訊,請參閱此 Stack Overflow 討論。
您可以在 SourceForge 頁面找到所有 ProGuard 版本。
範例 ProGuard 組態
兩個範例 ProGuard 組態檔如下所列。 請注意,在這些情況下,Xamarin.Android 建置處理序會提供輸入、輸出及程式庫 jar。 因此,您可以專注於其他選項,例如 -keep
。
簡單的 Android 活動
下列範例說明簡單 Android 活動的組態:
-injars bin/classes
-outjars bin/classes-processed.jar
-libraryjars /usr/local/java/android-sdk/platforms/android-9/android.jar
-dontpreverify
-repackageclasses ''
-allowaccessmodification
-optimizations !code/simplification/arithmetic
-keep public class mypackage.MyActivity
完整的 Android 應用程式
下列範例說明完整 Android 應用程式的組態:
-injars bin/classes
-injars libs
-outjars bin/classes-processed.jar
-libraryjars /usr/local/java/android-sdk/platforms/android-9/android.jar
-dontpreverify
-repackageclasses ''
-allowaccessmodification
-optimizations !code/simplification/arithmetic
-keepattributes *Annotation*
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
public void set*(...);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers class * implements android.os.Parcelable {
static android.os.Parcelable$Creator CREATOR;
}
-keepclassmembers class **.R$* {
public static <fields>;
}
ProGuard 與 Xamarin.Android 建置處理序
下列各節會說明 ProGuard 在 Xamarin.Android 發行建置期間的執行方式。
ProGuard 會執行什麼命令?
ProGuard 只是隨 Android SDK 提供的 .jar
。 因此它是以命令叫用:
java -jar proguard.jar options ...
ProGuard 工作
ProGuard 工作可在 Xamarin.Android.Build.Tasks.dll 組件內找到。 它是 _CompileToDalvikWithDx
目標的一部分,也是 _CompileDex
目標的一部分。
下列清單提供使用 [檔案>新專案] 建立新項目之後所產生的預設參數範例:
ProGuardJarPath = C:\Android\android-sdk\tools\proguard\lib\proguard.jar
AndroidSdkDirectory = C:\Android\android-sdk\
JavaToolPath = C:\Program Files (x86)\Java\jdk1.8.0_92\\bin
ProGuardToolPath = C:\Android\android-sdk\tools\proguard\
JavaPlatformJarPath = C:\Android\android-sdk\platforms\android-25\android.jar
ClassesOutputDirectory = obj\Release\android\bin\classes
AcwMapFile = obj\Release\acw-map.txt
ProGuardCommonXamarinConfiguration = obj\Release\proguard\proguard_xamarin.cfg
ProGuardGeneratedReferenceConfiguration = obj\Release\proguard\proguard_project_references.cfg
ProGuardGeneratedApplicationConfiguration = obj\Release\proguard\proguard_project_primary.cfg
ProGuardConfigurationFiles
{sdk.dir}tools\proguard\proguard-android.txt;
{intermediate.common.xamarin};
{intermediate.references};
{intermediate.application};
;
JavaLibrariesToEmbed = C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\MonoAndroid\v7.0\mono.android.jar
ProGuardJarInput = obj\Release\proguard\__proguard_input__.jar
ProGuardJarOutput = obj\Release\proguard\__proguard_output__.jar
DumpOutput = obj\Release\proguard\dump.txt
PrintSeedsOutput = obj\Release\proguard\seeds.txt
PrintUsageOutput = obj\Release\proguard\usage.txt
PrintMappingOutput = obj\Release\proguard\mapping.txt
接下來的範例說明從 IDE 執行的典型 ProGuard 命令:
C:\Program Files (x86)\Java\jdk1.8.0_92\\bin\java.exe -jar C:\Android\android-sdk\tools\proguard\lib\proguard.jar -include obj\Release\proguard\proguard_xamarin.cfg -include obj\Release\proguard\proguard_project_references.cfg -include obj\Release\proguard\proguard_project_primary.cfg "-injars 'obj\Release\proguard\__proguard_input__.jar';'C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\MonoAndroid\v7.0\mono.android.jar'" "-libraryjars 'C:\Android\android-sdk\platforms\android-25\android.jar'" -outjars "obj\Release\proguard\__proguard_output__.jar" -optimizations !code/allocation/variable
疑難排解
檔案問題
當 ProGuard 讀取其組態檔時,可能會顯示下列錯誤訊息:
Unknown option '-keep' in line 1 of file 'proguard.cfg'
此問題通常是在 Windows 上發生,原因是 .cfg
檔案的編碼錯誤。 ProGuard 無法處理可能會以文字檔呈現的「位元組順序標記」(BOM)。 如果有 BOM 存在,ProGuard 會隨即結束並出現上述錯誤。
若要避免這個問題,請從儲存檔案時允許不含 BOM 的文字編輯器中編輯自訂組態檔。 若要解決此問題,請確定文字編輯器將其編碼設定為 UTF-8
。 例如,在儲存檔案時,在文字編輯器 Notepad++ 選取 [編碼] > [編譯成 UTF-8 碼〈檔首無 BOM〉],即可儲存不含 BOM 的檔案。
其他問題
ProGuard 疑難排解 \(英文\) 頁面討論使 ProGuard 時會遇到的常見問題 (和解決方案)。
摘要
本指南說明 ProGuard 如何在 Xamarin.Android 中運作、如何在應用程式專案中予以啟用,以及如何加以設定。 文中提供範例 ProGuard 組態,並描述常見問題的解決方式。 如需 ProGuard 工具與 Android 的詳細資訊,請參閱壓縮程式碼和資源 \(英文\)。