ProGuard是一個Java類文件壓縮器坡锡、優(yōu)化器、混淆器和預(yù)校驗(yàn)器窒所。
壓縮步驟檢測并刪除未使用的類鹉勒、字段、方法和屬性吵取。優(yōu)化步驟分析和優(yōu)化方法的字節(jié)碼禽额。混淆步驟使用無意義的簡短名稱重命名其余的類皮官、字段和方法脯倒。這些第一步使代碼庫更小、更高效捺氢,并且更難進(jìn)行反向工程藻丢。最后一個預(yù)驗(yàn)證步驟將預(yù)驗(yàn)證信息添加到類中,這是Java微版本所必需的摄乒,或者可以縮短Java 6的啟動時間悠反。
這些步驟都是可選的。例如馍佑,ProGuard還可以僅用于列出應(yīng)用程序中的死代碼斋否,或者用于預(yù)驗(yàn)證類文件,以便在Java 6中高效使用拭荤。
ProGuard通常讀取輸入jar(或war茵臭、ear、zip或目錄)舅世。然后它會收縮笼恰、優(yōu)化、混淆和預(yù)先驗(yàn)證它們歇终∩缰ぃ可選地,可以執(zhí)行多個優(yōu)化傳遞评凝,每個優(yōu)化傳遞之后通常會執(zhí)行另一個收縮步驟追葡。ProGuard將處理后的結(jié)果寫入一個或多個輸出jar(或war、ear、zips或目錄)宜肉。輸入可能包含資源文件匀钧,其名稱和內(nèi)容可以選擇性地進(jìn)行更新,以反映混淆的類名谬返。
ProGuard需要指定輸入jar的庫jar(或war之斯、ear、zip或目錄)遣铝。這些基本上是編譯代碼所需的庫佑刷。ProGuard使用它們重構(gòu)正確處理所需的類依賴項。庫jar本身始終保持不變酿炸。您仍然應(yīng)該將它們放在最終應(yīng)用程序的類路徑中瘫絮。
入口點(diǎn)
為了確定哪些代碼必須保留,哪些代碼可以丟棄或混淆填硕,必須為代碼指定一個或多個入口點(diǎn)麦萤。這些入口點(diǎn)通常是帶有主方法、applet扁眯、midlet等的類壮莹。
- 在收縮步驟中,ProGuard從這些種子開始姻檀,遞歸地確定使用哪些類和類成員命满。所有其他類和類成員都將被丟棄。
- 在優(yōu)化步驟中施敢,ProGuard進(jìn)一步優(yōu)化代碼周荐。在其他優(yōu)化中狭莱,可以將非入口點(diǎn)的類和方法設(shè)置為私有的僵娃、靜態(tài)的或最終的,可以刪除未使用的參數(shù)腋妙,并且可以內(nèi)聯(lián)一些方法默怨。
- 在混淆步驟中,ProGuard重命名不是入口點(diǎn)的類和類成員骤素。在整個過程中匙睹,保留入口點(diǎn)可以確保仍然可以通過它們的原始名稱訪問它們。
- 預(yù)驗(yàn)證步驟是唯一不需要知道入口點(diǎn)的步驟济竹。
本手冊的使用部分描述了必要的-keep選項痕檬,示例部分提供了大量示例。
反射
對于任何代碼的自動處理送浊,反射和內(nèi)省都會帶來特定的問題梦谜。在ProGuard中,動態(tài)創(chuàng)建或調(diào)用(即按名稱)的代碼中的類或類成員也必須指定為入口點(diǎn)。例如唁桩,class . forname()構(gòu)造可以在運(yùn)行時引用任何類闭树。通常不可能預(yù)知必須保留哪些類(使用它們的原始名稱),因?yàn)轭惷赡軓呐渲梦募凶x取荒澡,例如报辱。因此,必須在ProGuard配置中使用相同的-keep選項指定它們单山。
但是碍现,ProGuard已經(jīng)為您檢測并處理以下情況:
- 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")
類和類成員的名稱當(dāng)然可能不同,但是構(gòu)造應(yīng)該完全相同饥侵,這樣ProGuard才能識別它們鸵赫。引用的類和類成員保存在收縮階段,字符串參數(shù)在混淆階段正確更新躏升。
此外辩棒,如果有必要保留一些類或類成員,ProGuard將提供一些建議膨疏。例如一睁,ProGuard會注意到這樣的構(gòu)造:“(SomeClass)Class.forName(variable). newinstance()”。這可能表明類或接口的SomeClass和/或其實(shí)現(xiàn)可能需要保留佃却。然后可以相應(yīng)地調(diào)整配置者吁。
要獲得適當(dāng)?shù)慕Y(jié)果,您至少應(yīng)該對正在處理的代碼有一定程度的熟悉饲帅。執(zhí)行大量反射的混淆代碼可能需要反復(fù)試驗(yàn)复凳,特別是在沒有關(guān)于代碼內(nèi)部結(jié)構(gòu)的必要信息的情況下。
Keep 可選項
-keep [,modifier,...] class_specification
指定要保留為代碼入口點(diǎn)的類和類成員(字段和方法)灶泵。例如育八,為了保留應(yīng)用程序,可以指定主類及其主方法赦邻。為了處理庫髓棋,您應(yīng)該指定所有可公開訪問的元素。
-keepclassmembers [,modifier,...] class_specification
指定要保留的類成員(如果它們的類也被保留的話)惶洲。例如按声,您可能希望保留實(shí)現(xiàn)Serializable接口的類的所有序列化字段和方法。
-keepclasseswithmembers [,modifier,...] class_specification
指定要保留的類和類成員恬吕,條件是所有指定的類成員都存在签则。例如,您可能希望保留所有具有 main 方法的應(yīng)用程序铐料,而不需要顯式地列出它們渐裂。
-keepnames class_specification
-keep, allowclass_specification 的縮寫
指定要保留其名稱的類和類成員(如果在收縮階段沒有刪除這些名稱)侨颈。例如,您可能希望保留實(shí)現(xiàn)Serializable接口的類的所有類名芯义,以便處理后的代碼與任何最初序列化的類保持兼容哈垢。完全不使用的類仍然可以被刪除。僅適用于模糊扛拨。
keepclassmembernames class_specification
- keepembers,allowshrinking class_specification 的縮寫
指定要保留其名稱(如果在收縮階段沒有刪除這些名稱)的類成員耘分。例如,在處理JDK 1.2或更早版本編譯的庫時绑警,您可能希望保留合成類$ methods的名稱求泰,這樣在處理使用處理過的庫的應(yīng)用程序時,混淆器可以再次檢測它(盡管ProGuard本身不需要這個)计盒。僅適用于模糊渴频。
-keepclasseswithmembernames class_specification
-keepclasseswithmembers,allowshrinking class_specification 的縮寫
指定要保留其名稱的類和類成員,條件是在收縮階段之后所有指定的類成員都出現(xiàn)北启。例如卜朗,您可能希望保留所有 native 方法名稱及其類的名稱,以便處理后的代碼仍然可以鏈接到本機(jī)庫代碼咕村。完全不使用的本地方法仍然可以被刪除场钉。如果使用了類文件,但是沒有使用它的本機(jī)方法懈涛,那么它的名稱仍然是模糊的逛万。僅適用于模糊。
-printseeds [filename]
指定詳細(xì)列出由各種-keep選項匹配的類和類成員批钠。列表被打印到標(biāo)準(zhǔn)輸出或給定文件中宇植。如果確實(shí)找到了想要的類成員,特別是在使用通配符的情況下埋心,這個列表非常有用指郁。例如,您可能想要列出您保存的所有應(yīng)用程序或所有applet踩窖。
壓縮 可選項
-dontshrink
指定不壓縮輸入類文件坡氯。默認(rèn)情況下晨横,應(yīng)用是開啟壓縮的;所有類和類成員都被刪除洋腮,除了各種-keep選項列出的類和它們直接或間接依賴的類。在每個優(yōu)化步驟之后還應(yīng)用收縮步驟手形,因?yàn)橐恍﹥?yōu)化可能會刪除更多的類和類成員啥供。
-printusage [filename]
指定列出輸入類文件的死代碼。列表被打印到標(biāo)準(zhǔn)輸出或給定文件中库糠。例如伙狐,您可以列出應(yīng)用程序未使用的代碼涮毫。僅適用于開啟壓縮時。
-whyareyoukeeping class_specification
指定打印有關(guān)為什么在收縮步驟中保留給定類和類成員的詳細(xì)信息贷屎。如果您想知道為什么輸出中會出現(xiàn)某些給定的元素罢防,那么這將非常有用。一般來說唉侄,有很多不同的原因咒吐。此選項為每個指定的類和類成員將最短的方法鏈打印到指定的種子或入口點(diǎn)。在當(dāng)前實(shí)現(xiàn)中属划,打印出來的最短鏈有時可能包含循環(huán)扣除額——這些扣除額并不反映實(shí)際的收縮過程恬叹。如果指定-verbose選項,則跟蹤包含完整的字段和方法簽名同眯。僅適用于開啟壓縮時绽昼。
優(yōu)化 可選項
-dontoptimize
指定不優(yōu)化輸入類文件。默認(rèn)情況下须蜗,優(yōu)化是啟用的;所有方法都在字節(jié)碼級別上進(jìn)行了優(yōu)化硅确。
-optimizations optimization_filter
在更細(xì)粒度的級別上指定要啟用和禁用的優(yōu)化。僅適用于優(yōu)化時明肮。這是一個專家的選擇疏魏。
-optimizationpasses n
指定要執(zhí)行的優(yōu)化傳遞的次數(shù)。默認(rèn)情況下晤愧,只執(zhí)行一次傳遞大莫。多次傳遞可能會導(dǎo)致進(jìn)一步的改進(jìn)。如果優(yōu)化通過后沒有發(fā)現(xiàn)任何改進(jìn)官份,則優(yōu)化結(jié)束只厘。僅適用于優(yōu)化時。
-assumenosideeffects class_specification
指定在處理過程中可以擴(kuò)展類和類成員的訪問修飾符舅巷。這可以改善優(yōu)化步驟的結(jié)果羔味。例如,在內(nèi)聯(lián)公共getter時钠右,可能還需要將訪問的字段也變?yōu)楣驳母吃1M管Java的二進(jìn)制兼容性規(guī)范在形式上不要求這樣做(cfr)。Java語言規(guī)范飒房,第二版搁凸,第13.4.6節(jié)),一些虛擬機(jī)將有問題的處理代碼狠毯,否則护糖。僅適用于優(yōu)化時(以及使用-repackageclasses選項混淆時)。
反指示:在處理要用作庫的代碼時嚼松,可能不應(yīng)該使用此選項嫡良,因?yàn)樵贏PI中設(shè)計為非公共的類和類成員可能會變成公共的锰扶。
-mergeinterfacesaggressively
指定可以合并接口,即使接口的實(shí)現(xiàn)類沒有實(shí)現(xiàn)所有接口方法寝受。這可以通過減少類的總數(shù)來減少輸出的大小坷牛。注意,Java的二進(jìn)制兼容性規(guī)范允許這樣的構(gòu)造(cfr)很澄。Java語言規(guī)范漓帅,第二版,第13.5.3節(jié))痴怨,即使它們在Java語言中是不允許的(cfr)忙干。Java語言規(guī)范,第二版浪藻,第8.1.4節(jié))捐迫。僅適用于優(yōu)化時。
反指示:設(shè)置此選項會降低某些jvm上處理的代碼的性能爱葵,因?yàn)楦呒壖磿r編譯傾向于使用較少實(shí)現(xiàn)類的更多接口施戴。更糟糕的是,一些jvm可能無法處理生成的代碼萌丈。值得注意的是:
Sun的JRE 1.3在一個類中遇到超過256個Miranda方法(沒有實(shí)現(xiàn)的接口方法)時赞哗,可能會拋出一個內(nèi)部錯誤。
混淆 可選項
-dontobfuscate
指定不混淆輸入類文件辆雾。默認(rèn)情況下肪笋,應(yīng)用混淆;類和類成員接收新的短隨機(jī)名稱,但不同的-keep選項列出的名稱除外度迂。刪除對調(diào)試有用的內(nèi)部屬性藤乙,如源文件名、變量名和行號惭墓。
-printmapping [filename]
指定為已重命名的類和類成員打印從舊名稱到新名稱的映射坛梁。映射被打印到標(biāo)準(zhǔn)輸出或給定文件中。例如腊凶,對于后續(xù)的增量混淆划咐,或者如果您希望再次理解混淆的堆棧跟蹤,就需要使用它钧萍。僅適用于開啟混淆褐缠。
-applymapping filename
指定重用在前一次混淆運(yùn)行ProGuard時打印出的給定名稱映射。映射文件中列出的類和類成員接收與其一起指定的名稱划煮。未提及的類和類成員將接收新名稱送丰。映射可以引用輸入類和庫類缔俄。此選項對于增量混淆非常有用弛秋,即處理現(xiàn)有代碼片段的外接程序或小補(bǔ)丁器躏。在這種情況下,您應(yīng)該考慮是否還需要選項—使用uniqueclassmembernames蟹略。只允許一個映射文件登失。僅適用于開啟混淆。
-obfuscationdictionary filename
指定一個文本文件挖炬,其中所有有效單詞都用作混淆字段和方法名稱揽浙。默認(rèn)情況下,短名稱如“a”意敛、“b”等用作混淆的名稱馅巷。通過使用混淆字典,您可以指定保留的關(guān)鍵字列表草姻,或者具有外部字符的標(biāo)識符钓猬。空格撩独、標(biāo)點(diǎn)符號敞曹、重復(fù)單詞和#符號后的注釋將被忽略。請注意综膀,混淆字典很難改善混淆澳迫。優(yōu)秀的編譯器可以自動替換它們,通過使用更簡單的名稱再次混淆剧劝,可以相當(dāng)簡單地恢復(fù)效果橄登。最有用的應(yīng)用程序是指定通常已經(jīng)出現(xiàn)在類文件中的字符串(如“代碼”),從而進(jìn)一步減小類文件的大小讥此。僅適用于開啟混淆示绊。
-classobfuscationdictionary filename
指定一個文本文件,其中所有有效單詞都用作混淆類名暂论∶婧郑混淆字典類似于選項之一-混淆字典。僅適用于開啟混淆取胎。
-packageobfuscationdictionary filename
指定一個文本文件展哭,其中所有有效的單詞都用作混淆的包名∥胖混淆字典類似于選項之一-混淆字典匪傍。僅適用于開啟混淆。
-overloadaggressively
指定在混淆時應(yīng)用主動重載觉痛。然后役衡,多個字段和方法可以獲得相同的名稱,只要它們的參數(shù)和返回類型不同(不僅僅是它們的參數(shù))薪棒。這個選項可以使處理后的代碼更小(更難以理解)手蝎。僅適用于開啟混淆榕莺。
反指示:生成的類文件屬于Java字節(jié)碼規(guī)范(cfr)。Java虛擬機(jī)規(guī)范棵介,第二版钉鸯,4.5節(jié)和4.6節(jié)的第一段),盡管這種重載在Java語言中是不允許的(cfr)邮辽。Java語言規(guī)范唠雕,第二版,8.3節(jié)和8.4.7節(jié))吨述。盡管如此岩睁,一些工具仍然存在問題。
-useuniqueclassmembernames
指定將相同的混淆名稱分配給具有相同名稱的類成員揣云,將不同的混淆名稱分配給具有不同名稱的類成員(針對每個給定的類成員簽名)笙僚。如果沒有這個選項,可以將更多的類成員映射到相同的短名稱灵再,如“a”肋层、“b”等。因此翎迁,該選項略微增加了生成的代碼的大小栋猖,但它確保在后續(xù)的增量混淆步驟中始終尊重保存的混淆名稱映射。
例如汪榔,考慮兩個不同的接口蒲拉,其中包含具有相同名稱和簽名的方法。如果沒有此選項痴腌,這些方法可能在第一個混淆步驟中獲得不同的混淆名稱雌团。如果隨后添加了一個包含實(shí)現(xiàn)這兩個接口的類的補(bǔ)丁,那么ProGuard將不得不在增量混淆步驟中為這兩個方法強(qiáng)制使用相同的方法名士聪。原始的混淆代碼被更改锦援,以保持生成的代碼一致。在初始混淆步驟中使用此選項剥悟,將永遠(yuǎn)不需要進(jìn)行此類重命名灵寺。
僅適用于開啟混淆。事實(shí)上区岗,如果您計劃執(zhí)行增量混淆略板,您可能希望避免完全收縮和優(yōu)化,因?yàn)檫@些步驟可以刪除或修改代碼中對以后的添加非常重要的部分慈缔。
-dontusemixedcaseclassnames
指定在混淆時不要生成混合大小寫的類名叮称。默認(rèn)情況下,混淆的類名可以包含大寫字符和小寫字符的混合。這將創(chuàng)建完全可接受和可用的jar瓤檐。只有在使用不區(qū)分大小寫的歸檔系統(tǒng)(例如Windows)在平臺上解壓縮jar時赂韵,解壓縮工具才可能讓名稱類似的類文件彼此覆蓋。解壓時自毀的代碼!真正想在Windows上解包jar的開發(fā)人員可以使用這個選項來關(guān)閉這個行為距帅。請注意右锨,混淆的jar將因此變得更大括堤。僅適用于開啟混淆碌秸。
-keeppackagenames [package_filter]
指定不混淆給定的包名稱∏那裕可選過濾器是一個以逗號分隔的包名列表讥电。包名可以包含?、和*通配符轧抗,它們的前面可以有!非元件恩敌。僅適用于開啟混淆。
-flattenpackagehierarchy [package_name]
指定通過將重命名的所有包移動到給定的父包中來重新包裝它們横媚。如果沒有參數(shù)或使用空字符串(")纠炮,包將被移動到根包中。這個選項是進(jìn)一步混淆包名稱的一個例子灯蝴。它可以使處理后的代碼更小恢口,更難以理解。僅適用于開啟混淆穷躁。
-repackageclasses [package_name]
指定通過將重命名的所有類文件移動到單個給定包中來重新打包耕肩。如果沒有參數(shù)或使用空字符串("),則該包將被完全刪除问潭。此選項覆蓋-扁平化包層次結(jié)構(gòu)選項猿诸。這是進(jìn)一步混淆包名稱的另一個例子。它可以使處理后的代碼更小狡忙,更難以理解梳虽。它的棄用名稱是-defaultpackage。僅適用于開啟混淆灾茁。
反指示:如果將在包目錄中查找資源文件的類轉(zhuǎn)移到其他地方怖辆,它們將不再正常工作。如果有疑問删顶,請不要使用此選項竖螃,以免影響包裝。
-keepattributes [attribute_filter]
指定保存所保存的參數(shù)名稱和方法類型逗余。這個選項實(shí)際上保留了調(diào)試屬性LocalVariableTable和LocalVariableTypeTable的精簡版本特咆。它在處理庫時非常有用。一些ide可以使用這些信息來幫助使用這個庫的開發(fā)人員,例如使用工具提示或自動完成腻格。僅適用于開啟混淆画拾。
-renamesourcefileattribute [string]
指定要放入類文件的SourceFile屬性(和SourceDir屬性)中的常量字符串。注意菜职,屬性一開始就必須存在青抛,因此還必須使用-keepattributes指令顯式地保存它。例如酬核,您可能希望處理后的庫和應(yīng)用程序生成有用的模糊堆棧跟蹤蜜另。僅適用于開啟混淆。
-adaptclassstrings [class_filter]
指定與類名對應(yīng)的字符串常量也應(yīng)該混淆嫡意。如果沒有過濾器举瑰,所有與類名對應(yīng)的字符串常量都會被調(diào)整。對于篩選器蔬螟,只有匹配篩選器的類中的字符串常量才會被調(diào)整此迅。例如,如果您的代碼包含大量引用類的硬編碼字符串旧巾,并且您不希望保留它們的名稱耸序,那么您可能希望使用這個選項。主要適用于混淆時鲁猩,雖然相應(yīng)的類也會自動保存在收縮步驟中坎怪。
-adaptresourcefilenames [file_filter]
根據(jù)相應(yīng)類文件(如果有)的混淆名稱,指定要重命名的資源文件绳匀。如果沒有篩選器芋忿,與類文件對應(yīng)的所有資源文件都將被重命名。使用篩選器疾棵,只重命名匹配的文件戈钢。例如,請參見處理資源文件是尔。僅適用于開啟混淆殉了。
-adaptresourcefilecontents [file_filter]
指定要更新其內(nèi)容的資源文件。資源文件中提到的任何類名都將根據(jù)相應(yīng)類(如果有的話)的混淆名稱進(jìn)行重命名拟枚。如果沒有篩選器薪铜,則更新所有資源文件的內(nèi)容。使用篩選器恩溅,只更新匹配的文件隔箍。資源文件使用平臺的默認(rèn)字符集進(jìn)行解析和編寫。您可以通過設(shè)置環(huán)境變量LANG或Java系統(tǒng)屬性file.encoding來更改這個默認(rèn)字符集脚乡。例如蜒滩,請參見處理資源文件。僅適用于開啟混淆。
預(yù)校驗(yàn) 選項
-dontpreverify
指定不預(yù)驗(yàn)證處理后的類文件俯艰。默認(rèn)情況下捡遍,如果類文件的目標(biāo)是Java微版本或Java 6或更高版本,則會預(yù)先驗(yàn)證它們竹握。對于Java微版本画株,需要預(yù)驗(yàn)證,因此如果指定此選項啦辐,則需要對處理后的代碼運(yùn)行外部預(yù)驗(yàn)證器谓传。對于Java 6,不需要(目前)預(yù)先驗(yàn)證昧甘,但是它提高了Java虛擬機(jī)中類加載的效率良拼。
-microedition
指定處理后的類文件以Java微版本為目標(biāo)战得。然后充边,preverifier將添加適當(dāng)?shù)腟tackMap屬性,這與Java標(biāo)準(zhǔn)版的默認(rèn)StackMapTable屬性不同常侦。例如浇冰,如果您正在處理midlet,則需要此選項聋亡。
一般 可選項
-verbose
指定在處理過程中寫入更多信息肘习。如果程序以異常終止,該選項將打印出整個堆棧跟蹤坡倔,而不僅僅是異常消息漂佩。
-dontnote [class_filter]
指定不打印有關(guān)配置中可能出現(xiàn)的錯誤或遺漏的注釋,如類名中的拼寫錯誤罪塔,或可能有用的選項缺失投蝉。可選過濾器是一個正則表達(dá)式;ProGuard不打印具有匹配名稱的類的注釋征堪。
-dontwarn [class_filter]
指定根本不警告未解析的引用和其他重要問題瘩缆。可選過濾器是一個正則表達(dá)式;ProGuard不會打印關(guān)于具有匹配名稱的類的警告佃蚜。忽視警告是危險的庸娱。例如,如果處理確實(shí)需要未解析的類或類成員谐算,那么處理后的代碼將不能正常工作熟尉。只有當(dāng)你知道你在做什么時才使用這個選項!
-ignorewarnings
指定打印關(guān)于未解析引用和其他重要問題的警告,但在任何情況下都要繼續(xù)處理洲脂。忽視警告是危險的斤儿。例如,如果處理確實(shí)需要未解析的類或類成員,那么處理后的代碼將不能正常工作雇毫。只有當(dāng)你知道你在做什么時才使用這個選項!
-printconfiguration [filename]
指定用包含的文件和替換的變量寫出已解析的整個配置玄捕。結(jié)構(gòu)被打印到標(biāo)準(zhǔn)輸出或給定文件中。有時棚放,這對于調(diào)試配置或?qū)ML配置轉(zhuǎn)換為更具可讀性的格式非常有用枚粘。
-dump [filename]
指定在任何處理之后寫出類文件的內(nèi)部結(jié)構(gòu)。結(jié)構(gòu)被打印到標(biāo)準(zhǔn)輸出或給定文件中飘蚯。例如馍迄,您可能想要寫出給定jar文件的內(nèi)容,而根本不需要處理它局骤。