ProGuard-壓縮-混淆
ProGuard是一個Java類檔案壓縮器、優化器、混淆器和預校驗器。
壓縮步驟檢測並刪除未使用的類、欄位、方法和屬性。優化步驟分析和優化方法的位元組碼。混淆步驟使用無意義的簡短名稱重新命名其餘的類、欄位和方法。這些第一步使程式碼庫更小、更高效,並且更難進行反向工程。最後一個預驗證步驟將預驗證資訊新增到類中,這是Java微版本所必需的,或者可以縮短Java 6的啟動時間。
這些步驟都是可選的。例如,ProGuard還可以僅用於列出應用程式中的死程式碼,或者用於預驗證類檔案,以便在Java 6中高效使用。

proguard1.png
ProGuard通常讀取輸入jar(或war、ear、zip或目錄)。然後它會收縮、優化、混淆和預先驗證它們。可選地,可以執行多個優化傳遞,每個優化傳遞之後通常會執行另一個收縮步驟。ProGuard將處理後的結果寫入一個或多個輸出jar(或war、ear、zips或目錄)。輸入可能包含資原始檔,其名稱和內容可以選擇性地進行更新,以反映混淆的類名。
ProGuard需要指定輸入jar的庫jar(或war、ear、zip或目錄)。這些基本上是編譯程式碼所需的庫。ProGuard使用它們重構正確處理所需的類依賴項。庫jar本身始終保持不變。您仍然應該將它們放在最終應用程式的類路徑中。
入口點
為了確定哪些程式碼必須保留,哪些程式碼可以丟棄或混淆,必須為程式碼指定一個或多個入口點。這些入口點通常是帶有主方法、applet、midlet等的類。
- 在收縮步驟中,ProGuard從這些種子開始,遞迴地確定使用哪些類和類成員。所有其他類和類成員都將被丟棄。
- 在優化步驟中,ProGuard進一步優化程式碼。在其他優化中,可以將非入口點的類和方法設定為私有的、靜態的或最終的,可以刪除未使用的引數,並且可以內聯一些方法。
- 在混淆步驟中,ProGuard重新命名不是入口點的類和類成員。在整個過程中,保留入口點可以確保仍然可以通過它們的原始名稱訪問它們。
- 預驗證步驟是唯一不需要知道入口點的步驟。
本手冊的使用部分描述了必要的-keep選項,示例部分提供了大量示例。
反射
對於任何程式碼的自動處理,反射和內省都會帶來特定的問題。在ProGuard中,動態建立或呼叫(即按名稱)的程式碼中的類或類成員也必須指定為入口點。例如,class . forname()構造可以在執行時引用任何類。通常不可能預知必須保留哪些類(使用它們的原始名稱),因為類名可能從配置檔案中讀取,例如。因此,必須在ProGuard配置中使用相同的-keep選項指定它們。
但是,ProGuard已經為您檢測並處理以下情況:
- Class.forName("SomeClass")
- SomeClass.class
- SomeClass.class.getField("someField")
- SomeClass.class.getDeclaredField("someField")
- SomeClass.class.getMethod("someMethod", new Class[] {})
- SomeClass.class.getMethod("someMethod", new Class[] { A.class })
- SomeClass.class.getMethod("someMethod", new Class[] { A.class, B.class })
- SomeClass.class.getDeclaredMethod("someMethod", new Class[] {})
- SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class })
- SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class, B.class })
- AtomicIntegerFieldUpdater.newUpdater(SomeClass.class, "someField")
- AtomicLongFieldUpdater.newUpdater(SomeClass.class, "someField")
- AtomicReferenceFieldUpdater.newUpdater(SomeClass.class, SomeType.class, "someField")
類和類成員的名稱當然可能不同,但是構造應該完全相同,這樣ProGuard才能識別它們。引用的類和類成員儲存在收縮階段,字串引數在混淆階段正確更新。
此外,如果有必要保留一些類或類成員,ProGuard將提供一些建議。例如,ProGuard會注意到這樣的構造:“(SomeClass)Class.forName(variable). newinstance()”。這可能表明類或介面的SomeClass和/或其實現可能需要保留。然後可以相應地調整配置。
要獲得適當的結果,您至少應該對正在處理的程式碼有一定程度的熟悉。執行大量反射的混淆程式碼可能需要反覆試驗,特別是在沒有關於程式碼內部結構的必要資訊的情況下。
Keep 可選項
-keep [,modifier,...] class_specification
指定要保留為程式碼入口點的類和類成員(欄位和方法)。例如,為了保留應用程式,可以指定主類及其主方法。為了處理庫,您應該指定所有可公開訪問的元素。
-keepclassmembers [,modifier,...] class_specification
指定要保留的類成員(如果它們的類也被保留的話)。例如,您可能希望保留實現Serializable介面的類的所有序列化欄位和方法。
-keepclasseswithmembers [,modifier,...] class_specification
指定要保留的類和類成員,條件是所有指定的類成員都存在。例如,您可能希望保留所有具有 main 方法的應用程式,而不需要顯式地列出它們。
-keepnames class_specification
-keep, allowclass_specification 的縮寫
指定要保留其名稱的類和類成員(如果在收縮階段沒有刪除這些名稱)。例如,您可能希望保留實現Serializable介面的類的所有類名,以便處理後的程式碼與任何最初序列化的類保持相容。完全不使用的類仍然可以被刪除。僅適用於模糊。
keepclassmembernames class_specification
- keepembers,allowshrinking class_specification 的縮寫
指定要保留其名稱(如果在收縮階段沒有刪除這些名稱)的類成員。例如,在處理JDK 1.2或更早版本編譯的庫時,您可能希望保留合成類$ methods的名稱,這樣在處理使用處理過的庫的應用程式時,混淆器可以再次檢測它(儘管ProGuard本身不需要這個)。僅適用於模糊。
-keepclasseswithmembernames class_specification
-keepclasseswithmembers,allowshrinking class_specification 的縮寫
指定要保留其名稱的類和類成員,條件是在收縮階段之後所有指定的類成員都出現。例如,您可能希望保留所有 native 方法名稱及其類的名稱,以便處理後的程式碼仍然可以連結到本機庫程式碼。完全不使用的本地方法仍然可以被刪除。如果使用了類檔案,但是沒有使用它的本機方法,那麼它的名稱仍然是模糊的。僅適用於模糊。
-printseeds [filename]
指定詳細列出由各種-keep選項匹配的類和類成員。列表被列印到標準輸出或給定檔案中。如果確實找到了想要的類成員,特別是在使用萬用字元的情況下,這個列表非常有用。例如,您可能想要列出您儲存的所有應用程式或所有applet。
壓縮 可選項
-dontshrink
指定不壓縮輸入類檔案。預設情況下,應用是開啟壓縮的;所有類和類成員都被刪除,除了各種-keep選項列出的類和它們直接或間接依賴的類。在每個優化步驟之後還應用收縮步驟,因為一些優化可能會刪除更多的類和類成員。
-printusage [filename]
指定列出輸入類檔案的死程式碼。列表被列印到標準輸出或給定檔案中。例如,您可以列出應用程式未使用的程式碼。僅適用於開啟壓縮時。
-whyareyoukeeping class_specification
指定列印有關為什麼在收縮步驟中保留給定類和類成員的詳細資訊。如果您想知道為什麼輸出中會出現某些給定的元素,那麼這將非常有用。一般來說,有很多不同的原因。此選項為每個指定的類和類成員將最短的方法鏈列印到指定的種子或入口點。在當前實現中,打印出來的最短鏈有時可能包含循環扣除額——這些扣除額並不反映實際的收縮過程。如果指定-verbose選項,則跟蹤包含完整的欄位和方法簽名。僅適用於開啟壓縮時。
優化 可選項
-dontoptimize
指定不優化輸入類檔案。預設情況下,優化是啟用的;所有方法都在位元組碼級別上進行了優化。
-optimizations optimization_filter
在更細粒度的級別上指定要啟用和禁用的優化。僅適用於優化時。這是一個專家的選擇。
-optimizationpasses n
指定要執行的優化傳遞的次數。預設情況下,只執行一次傳遞。多次傳遞可能會導致進一步的改進。如果優化通過後沒有發現任何改進,則優化結束。僅適用於優化時。
-assumenosideeffects class_specification
指定在處理過程中可以擴充套件類和類成員的訪問修飾符。這可以改善優化步驟的結果。例如,在內聯公共getter時,可能還需要將訪問的欄位也變為公共的。儘管Java的二進位制相容性規範在形式上不要求這樣做(cfr)。Java語言規範,第二版,第13.4.6節),一些虛擬機器將有問題的處理程式碼,否則。僅適用於優化時(以及使用-repackageclasses選項混淆時)。
反指示:在處理要用作庫的程式碼時,可能不應該使用此選項,因為在API中設計為非公共的類和類成員可能會變成公共的。
-mergeinterfacesaggressively
指定可以合併介面,即使介面的實現類沒有實現所有介面方法。這可以通過減少類的總數來減少輸出的大小。注意,Java的二進位制相容性規範允許這樣的構造(cfr)。Java語言規範,第二版,第13.5.3節),即使它們在Java語言中是不允許的(cfr)。Java語言規範,第二版,第8.1.4節)。僅適用於優化時。
反指示:設定此選項會降低某些jvm上處理的程式碼的效能,因為高階即時編譯傾向於使用較少實現類的更多介面。更糟糕的是,一些jvm可能無法處理生成的程式碼。值得注意的是:
Sun的JRE 1.3在一個類中遇到超過256個Miranda方法(沒有實現的介面方法)時,可能會丟擲一個內部錯誤。
混淆 可選項
-dontobfuscate
指定不混淆輸入類檔案。預設情況下,應用混淆;類和類成員接收新的短隨機名稱,但不同的-keep選項列出的名稱除外。刪除對除錯有用的內部屬性,如原始檔名、變數名和行號。
-printmapping [filename]
指定為已重新命名的類和類成員列印從舊名稱到新名稱的對映。對映被列印到標準輸出或給定檔案中。例如,對於後續的增量混淆,或者如果您希望再次理解混淆的堆疊跟蹤,就需要使用它。僅適用於開啟混淆。
-applymapping filename
指定重用在前一次混淆執行ProGuard時打印出的給定名稱對映。對映檔案中列出的類和類成員接收與其一起指定的名稱。未提及的類和類成員將接收新名稱。對映可以引用輸入類和庫類。此選項對於增量混淆非常有用,即處理現有程式碼片段的外接程式或小補丁。在這種情況下,您應該考慮是否還需要選項—使用uniqueclassmembernames。只允許一個對映檔案。僅適用於開啟混淆。
-obfuscationdictionary filename
指定一個文字檔案,其中所有有效單詞都用作混淆欄位和方法名稱。預設情況下,短名稱如“a”、“b”等用作混淆的名稱。通過使用混淆字典,您可以指定保留的關鍵字列表,或者具有外部字元的識別符號。空格、標點符號、重複單詞和#符號後的註釋將被忽略。請注意,混淆字典很難改善混淆。優秀的編譯器可以自動替換它們,通過使用更簡單的名稱再次混淆,可以相當簡單地恢復效果。最有用的應用程式是指定通常已經出現在類檔案中的字串(如“程式碼”),從而進一步減小類檔案的大小。僅適用於開啟混淆。
-classobfuscationdictionary filename
指定一個文字檔案,其中所有有效單詞都用作混淆類名。混淆字典類似於選項之一-混淆字典。僅適用於開啟混淆。
-packageobfuscationdictionary filename
指定一個文字檔案,其中所有有效的單詞都用作混淆的包名。混淆字典類似於選項之一-混淆字典。僅適用於開啟混淆。
-overloadaggressively
指定在混淆時應用主動過載。然後,多個欄位和方法可以獲得相同的名稱,只要它們的引數和返回型別不同(不僅僅是它們的引數)。這個選項可以使處理後的程式碼更小(更難以理解)。僅適用於開啟混淆。
反指示:生成的類檔案屬於Java位元組碼規範(cfr)。Java虛擬機器規範,第二版,4.5節和4.6節的第一段),儘管這種過載在Java語言中是不允許的(cfr)。Java語言規範,第二版,8.3節和8.4.7節)。儘管如此,一些工具仍然存在問題。
-useuniqueclassmembernames
指定將相同的混淆名稱分配給具有相同名稱的類成員,將不同的混淆名稱分配給具有不同名稱的類成員(針對每個給定的類成員簽名)。如果沒有這個選項,可以將更多的類成員對映到相同的短名稱,如“a”、“b”等。因此,該選項略微增加了生成的程式碼的大小,但它確保在後續的增量混淆步驟中始終尊重儲存的混淆名稱對映。
例如,考慮兩個不同的介面,其中包含具有相同名稱和簽名的方法。如果沒有此選項,這些方法可能在第一個混淆步驟中獲得不同的混淆名稱。如果隨後添加了一個包含實現這兩個介面的類的補丁,那麼ProGuard將不得不在增量混淆步驟中為這兩個方法強制使用相同的方法名。原始的混淆程式碼被更改,以保持生成的程式碼一致。在初始混淆步驟中使用此選項,將永遠不需要進行此類重新命名。
僅適用於開啟混淆。事實上,如果您計劃執行增量混淆,您可能希望避免完全收縮和優化,因為這些步驟可以刪除或修改程式碼中對以後的新增非常重要的部分。
-dontusemixedcaseclassnames
指定在混淆時不要生成混合大小寫的類名。預設情況下,混淆的類名可以包含大寫字元和小寫字元的混合。這將建立完全可接受和可用的jar。只有在使用不區分大小寫的歸檔系統(例如Windows)在平臺上解壓縮jar時,解壓縮工具才可能讓名稱類似的類檔案彼此覆蓋。解壓時自毀的程式碼!真正想在Windows上解包jar的開發人員可以使用這個選項來關閉這個行為。請注意,混淆的jar將因此變得更大。僅適用於開啟混淆。
-keeppackagenames [package_filter]
指定不混淆給定的包名稱。可選過濾器是一個以逗號分隔的包名列表。包名可以包含?、 和 *萬用字元,它們的前面可以有!非元件。僅適用於開啟混淆。
-flattenpackagehierarchy [package_name]
指定通過將重新命名的所有包移動到給定的父包中來重新包裝它們。如果沒有引數或使用空字串("),包將被移動到根包中。這個選項是進一步混淆包名稱的一個例子。它可以使處理後的程式碼更小,更難以理解。僅適用於開啟混淆。
-repackageclasses [package_name]
指定通過將重新命名的所有類檔案移動到單個給定包中來重新打包。如果沒有引數或使用空字串("),則該包將被完全刪除。此選項覆蓋-扁平化包層次結構選項。這是進一步混淆包名稱的另一個例子。它可以使處理後的程式碼更小,更難以理解。它的棄用名稱是-defaultpackage。僅適用於開啟混淆。
反指示:如果將在包目錄中查詢資原始檔的類轉移到其他地方,它們將不再正常工作。如果有疑問,請不要使用此選項,以免影響包裝。
-keepattributes [attribute_filter]
指定儲存所儲存的引數名稱和方法型別。這個選項實際上保留了除錯屬性LocalVariableTable和LocalVariableTypeTable的精簡版本。它在處理庫時非常有用。一些ide可以使用這些資訊來幫助使用這個庫的開發人員,例如使用工具提示或自動完成。僅適用於開啟混淆。
-renamesourcefileattribute [string]
指定要放入類檔案的SourceFile屬性(和SourceDir屬性)中的常量字串。注意,屬性一開始就必須存在,因此還必須使用-keepattributes指令顯式地儲存它。例如,您可能希望處理後的庫和應用程式生成有用的模糊堆疊跟蹤。僅適用於開啟混淆。
-adaptclassstrings [class_filter]
指定與類名對應的字串常量也應該混淆。如果沒有過濾器,所有與類名對應的字串常量都會被調整。對於篩選器,只有匹配篩選器的類中的字串常量才會被調整。例如,如果您的程式碼包含大量引用類的硬編碼字串,並且您不希望保留它們的名稱,那麼您可能希望使用這個選項。主要適用於混淆時,雖然相應的類也會自動儲存在收縮步驟中。
-adaptresourcefilenames [file_filter]
根據相應類檔案(如果有)的混淆名稱,指定要重新命名的資原始檔。如果沒有篩選器,與類檔案對應的所有資原始檔都將被重新命名。使用篩選器,只重新命名匹配的檔案。例如,請參見處理資原始檔。僅適用於開啟混淆。
-adaptresourcefilecontents [file_filter]
指定要更新其內容的資原始檔。資原始檔中提到的任何類名都將根據相應類(如果有的話)的混淆名稱進行重新命名。如果沒有篩選器,則更新所有資原始檔的內容。使用篩選器,只更新匹配的檔案。資原始檔使用平臺的預設字符集進行解析和編寫。您可以通過設定環境變數LANG或Java系統屬性file.encoding來更改這個預設字符集。例如,請參見處理資原始檔。僅適用於開啟混淆。
預校驗 選項
-dontpreverify
指定不預驗證處理後的類檔案。預設情況下,如果類檔案的目標是Java微版本或Java 6或更高版本,則會預先驗證它們。對於Java微版本,需要預驗證,因此如果指定此選項,則需要對處理後的程式碼執行外部預驗證器。對於Java 6,不需要(目前)預先驗證,但是它提高了Java虛擬機器中類載入的效率。
-microedition
指定處理後的類檔案以Java微版本為目標。然後,preverifier將新增適當的StackMap屬性,這與Java標準版的預設StackMapTable屬性不同。例如,如果您正在處理midlet,則需要此選項。
一般 可選項
-verbose
指定在處理過程中寫入更多資訊。如果程式以異常終止,該選項將打印出整個堆疊跟蹤,而不僅僅是異常訊息。
-dontnote [class_filter]
指定不列印有關配置中可能出現的錯誤或遺漏的註釋,如類名中的拼寫錯誤,或可能有用的選項缺失。可選過濾器是一個正則表示式;ProGuard不列印具有匹配名稱的類的註釋。
-dontwarn [class_filter]
指定根本不警告未解析的引用和其他重要問題。可選過濾器是一個正則表示式;ProGuard不會列印關於具有匹配名稱的類的警告。忽視警告是危險的。例如,如果處理確實需要未解析的類或類成員,那麼處理後的程式碼將不能正常工作。只有當你知道你在做什麼時才使用這個選項!
-ignorewarnings
指定列印關於未解析引用和其他重要問題的警告,但在任何情況下都要繼續處理。忽視警告是危險的。例如,如果處理確實需要未解析的類或類成員,那麼處理後的程式碼將不能正常工作。只有當你知道你在做什麼時才使用這個選項!
-printconfiguration [filename]
指定用包含的檔案和替換的變數寫出已解析的整個配置。結構被列印到標準輸出或給定檔案中。有時,這對於除錯配置或將XML配置轉換為更具可讀性的格式非常有用。
-dump [filename]
指定在任何處理之後寫出類檔案的內部結構。結構被列印到標準輸出或給定檔案中。例如,您可能想要寫出給定jar檔案的內容,而根本不需要處理它。