proguard流程javascript
-dontshrink
聲明不進行壓縮操做,默認狀況下,除了-keep配置(下詳)的類及其直接或間接引用到的類,都會被移除。java
-dontoptimize
不對class進行優化,默認開啓優化。
注意:因爲優化會進行類合併、內聯等多種優化,-applymapping可能沒法徹底應用,需使用熱修復的應用,建議使用此配置關閉優化。android
-optimizationpassesn
執行優化的次數,默認1次,屢次能達到更好的優化效果。web
-optimizationsoptimization_filter
優化配置,可進行字段優化、內聯、類合併、代碼簡化、算法指令精簡等操做。正則表達式
#只進行 移除未使用的局部變量、算法指令精簡 -optimizations code/removal/variable,code/simplification/arithmetic #進行除 算法指令精簡、字段、類合併外的全部優化 -optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-dontobfuscate
不進行混淆,默認開啓混淆。除-keep指定的類及成員外,都會被替換成簡短、隨機的名稱,以達到混淆的目的。算法
-applymappingfilename
根據指定的mapping映射文件進行混淆。數組
-obfuscationdictionaryfilename
指定字段、方法名的混淆字典,默認狀況下使用abc等字母組合,好比根據本身的喜愛指定中文、特殊字符進行混淆命名。app
-classobfuscationdictionaryfilename
指定類名混淆字典。ide
-packageobfuscationdictionaryfilename
指定包名混淆字典。測試
-useuniqueclassmembernames
指定相同的混淆名稱對應不一樣類的相同成員,不一樣的混淆名稱對應不一樣的類成員。在沒有指定這個選項時,不一樣類的不一樣方法均可能映射到a,b,c。
有一種狀況,好比兩個不一樣的接口,擁有相同的方法簽名,在沒有指定這個選項時,這兩個接口的方法可能混淆成不一樣的名稱。但若是新增一個類同時實現這兩個接口,而且利用-applymapping指定以前的mapping映射文件時,這兩個接口的方法必須混淆成相同的名稱,這時就和以前的mapping衝突了。
在某此熱修復場景下須要指定此選項。
-dontusemixedcaseclassnames
指定不使用大小寫混用的類名,默認狀況下混淆後的類名可能同時包含大寫小字母。這在某些對大小寫不敏感的系統(如windowns)上解壓時,可能存在文件被相互覆蓋的狀況。
-keeppackagenames[package_filter]
指定不混淆指定的包名,多個包名能夠用逗號分隔,可使用? * **通配符,而且可使用否認符(!)。
-keepattributes[attribute_filter]
指定保留屬性,多個屬性能夠用多個-keepattributes配置,也能夠用逗號分隔,可使用? * **通配符,而且可使用否認符(!)。
好比,在混淆ibrary庫時,應該至少keep Exceptions, InnerClasses, Signature;若是在追蹤代碼,還須要keep符號表;使用到註解時也須要keep。
-keepattributes Exceptions,InnerClasses,Signature -keepattributes SourceFile,LineNumberTable -keepattributes *Annotation*
-keepparameternames
指定keep已經被keep的方法的參數類型和參數名稱,在混淆library庫時很是有用,可供IDE幫助用戶進行信息提示和代碼自動填充。
-dontpreverify
指定不對class進行預校驗,默認狀況下,在編譯版本爲micro或者1.6或更高版本時是開啓的。但編譯成Android版本時,預校驗是沒必要須的,配置這個選項能夠節省一點編譯時間。
(Android會把class編譯成dex,並對dex文件進行校驗,對class進行預校驗是多餘的。)
-keep[,modifier,...] class_specification
指定類及類成員做爲代碼入口,保護其不被proguard,如:
-keep class com.rush.Test -keep interface com.rush.InterfaceTest -keep class com.rush.** { <init>; public <fields>; public <methods>; public *** get*(); void set*(***); }
類名 通配符以下:
| 通配符 | 含義 |
| --- |
| ? | 匹配單個字符,包名分隔符(.)除外 |
| * | 匹配除(.)外的任意字符 |
| ** | 匹配任意字符(包含.),如com.rush.**匹配com.rush包下的全部類及其全部子包的類。 |
字段和方法 通配符以下:
| 通配符 | 含義 |
| --- |
| <init> | 匹配全部構造方法 |
| <fields> | 匹配全部字段 |
| <methods> | 匹配全部方法 |
| ? | 匹配單個字符,包名分隔符(.)除外 |
| * | 匹配除(.)外的任意字符 |
類型 通配符以下:
| 通配符 | 含義 |
| --- |
| % | 匹配原始類型,如int, boolean等 |
| ? | 匹配任意單個字符 |
| * | 匹配除包名分隔符(.)外的任意字符 |
| ** | 匹配任意字符,包括包名分隔符(.) |
| *** | 匹配任意類型(原始類型、非原始類型、數組或非數組類型)|
| ... | 匹配任意參數個數,任意參數類型 |
其中類配置完整定義以下,其中[]表示可選:
[@annotationtype] [[!]public|final|abstract|@ ...] [!]interface|class|enum classname [extends|implements [@annotationtype] classname] [{ [@annotationtype] [[!]public|private|protected|static|volatile|transient ...] <fields> | (fieldtype fieldname); [@annotationtype] [[!]public|private|protected|static|synchronized|native|abstract|strictfp ...] <methods> | <init>(argumenttype,...) | classname(argumenttype,...) | (returntype methodname(argumenttype,...)); [@annotationtype] [[!]public|private|protected|static ... ] *; ... }]
keep過於簡單粗暴,proguard提供了6種不一樣的配置:
| 保留 | 防止被移除或重命名 | 防止被重命名(未使用的會被移除) |
| --- | --- |
| 類和類成員 | -keep | -keepnames |
| 僅類成員 | -keepclassmembers | -keepclassmembernames |
| 如類含有某成員,保留類及其成員 | -keepclasseswithmembers | -keepclasseswithmembernames |
-verbose
指定在混淆過程當中輸出更多信息,配置這個選項後,在遇到異常時,將輸出完整的堆棧,而不只僅是異常消息。
-dontnote[class_filter]
指定不輸出潛在的錯誤或者遺漏,好比拼寫錯誤或者缺失了有用的信息。class_filter是一個正則表達式,匹配到類將不輸出這些信息。
-dontwarn[class_filter]
指定一組類,不警告這些類中找不到引用或其它重要的問題。這個選項是很危險的,好比,找不到引用的錯誤可能致使代碼不能正常work。
(在引用一些存在警告的jar包,這個選項比較有用。)
-ignorewarnings
指定輸出因此警告信息,但繼續進行混淆。同上一選項,慎用。
-printconfiguration[filename]
指定輸出整個過程當中的全部配置,輸出到標準輸出流或者指定文件中。這有時候在調度配置時有用。
-dump[filename]
指定在任一處理過程後,輸出class文件的結構,能夠輸出到標準輸出流或者指定文件中。
在IDE自動生成的project.properties文件中,有這樣一行:
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
Android Studio默認生成的build.gradle文件有以下配置:
buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }
其中getDefaultProguardFile('proguard-android.txt')獲取的也是tools/proguard/proguard-android.txt。下面看一下這個文件的配置:
# 不使用大小寫混合類名 -dontusemixedcaseclassnames # 不路過引用庫中的非public類 -dontskipnonpubliclibraryclasses # 輸出更多信息 -verbose # 不進行優化 -dontoptimize # 不進行預校驗 -dontpreverify # keep註解 -keepattributes *Annotation* #keep google license服務接口 -keep public class com.google.vending.licensing.ILicensingService -keep public class com.android.vending.licensing.ILicensingService # keep native方法及其所屬類 -keepclasseswithmembernames class * { native <methods>; } # keep自定義view的get/set方法 -keepclassmembers public class * extends android.view.View { void set*(***); *** get*(); } # keep繼續自Activity中全部包含public void *(android.view.View)簽名的方法,如onClick -keepclassmembers class * extends android.app.Activity { public void *(android.view.View); } # keep枚舉中的values和valueOf方法 -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); } # keep Parcelable的CREATOR成員 -keepclassmembers class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator CREATOR; } # keep R文件的靜態字段 -keepclassmembers class **.R$* { public static <fields>; } # 不輸出support包中的警告 -dontwarn android.support.**
示例引用自官方文檔samples
# 保存mapping映射文件到out.map -printmapping out.map # keep已keep方法的參數類型及參數名稱 -keepparameternames # 這個配置未弄清楚,待測試 -renamesourcefileattribute SourceFile -keepattributes Exceptions,InnerClasses,Signature,Deprecated, SourceFile,LineNumberTable,*Annotation*,EnclosingMethod # keep全部類的protected成員 -keep public class * { public protected *; } # keep在jdk 1.2中編譯器插入的代碼 -keepclassmembernames class * { java.lang.Class class$(java.lang.String); java.lang.Class class$(java.lang.String, boolean); } # keep native方法 -keepclasseswithmembernames,includedescriptorclasses class * { native <methods>; } # keep枚舉中的values和valueOf方法 -keepclassmembers,allowoptimization enum * { public static **[] values(); public static ** valueOf(java.lang.String); } # keep系列化相關方法 -keepclassmembers class * implements java.io.Serializable { static final long serialVersionUID; private static final java.io.ObjectStreamField[] serialPersistentFields; private void writeObject(java.io.ObjectOutputStream); private void readObject(java.io.ObjectInputStream); java.lang.Object writeReplace(); java.lang.Object readResolve(); }
示例引用自官方文檔samples
-dontpreverify -repackageclasses '' -allowaccessmodification # 不優化算法指令 -optimizations !code/simplification/arithmetic -keepattributes *Annotation* # keep繼承自系統組件的類 -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自定義view及其構造方法、set方法 -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 * extends android.content.Context { public void *(android.view.View); public void *(android.view.MenuItem); } -keepclassmembers class * implements android.os.Parcelable { static ** CREATOR; } -keepclassmembers class **.R$* { public static <fields>; } # keep javascript註釋的方法,使用到webview js回調方法的須要添加此配置 -keepclassmembers class * { @android.webkit.JavascriptInterface <methods>; }
並非全部會被反射引用的類都必須keep,在progurad過程當中能直接分析到引用的類會被proguard作相應的處理:
# Class.forName的類名"SomeClass"被混淆後自動替換 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")
寫個demo驗證下:
Class<?> clazz = Class.forName("com.rush.test.SimpleClass1"); clazz.getDeclaredMethod("Test1"); SimpleClass2.class.getDeclaredField("mTestField"); SimpleClass2.class.getDeclaredMethod("Test2");
對以上代碼編譯並proguard,結果以下:
Class.forName("com.rush.a.a").getDeclaredMethod("Test1", new Class[0]); b.class.getDeclaredField("a"); b.class.getDeclaredMethod("a", new Class[0]);
從結果看,反射並非你們想像的那樣必須keep,proguard能自動分析到引用的狀況都能正確處理。但有些類是在配置文件裏配置,或者動態拼接類名反射的,這些狀況須要作好keep。
爲了問題追蹤的方便,建議全部會被反射引用的代碼和library public接口都作好keep。
全部會被反射引用的類都作好keep(建議,雖然有些反射能被正確處理)。
如native方法,四大組件,接口model,枚舉,序列化類等。
只keep必須保留的內容,不要過分keep
使用熱修復的App,添加-dontoptimize配置
做者:rushjs 連接:https://www.jianshu.com/p/d768f6d1d93b 來源:簡書 簡書著做權歸做者全部,任何形式的轉載都請聯繫做者得到受權並註明出處。