ProGuard可以經過壓縮、優化、混淆、預檢等操做,檢測並刪除未使用的類,字段,方法和屬性,分析和優化字節碼,使用簡短無心義的名稱來重命名類,字段和方法。從而使代碼更小、更高效、更難進行逆向工程。 html
上圖就是ProGuard的工做流程,分別會通過四個階段:java
壓縮(Shrink)
:在壓縮處理這一步中,用於檢測和刪除沒有使用的類,字段,方法和屬性優化(Optimize)
:在優化處理這一步中,對字節碼進行優化,而且移除無用指令混淆(Obfuscate)
:在混淆處理這一步中,使用a,b,c等無心義的名稱,對類,字段和方法進行重命名預檢(Preveirfy)
:在預檢這一步中,主要是在Java平臺上對處理後的代碼進行預檢以上四個步驟都是可選的,咱們能夠經過配置腳原本決定其中的哪幾個步驟。好比咱們能夠配置只壓縮和混淆,不進行優化,不進行預檢。 ProGuard的官網有使用指導:proguard.sourceforge.net/android
ProGuard能夠經過命令行調用,如:web
執行成功後,用jd-gui打開處理後的jar文件:算法
能夠發現,類已經被混淆處理了。windows
這裏,咱們引入Entry points的概念。Entry points是在ProGuard過程當中不會處理的類或者方法。 在Shrink的步驟中,ProGuard會遞歸遍歷,搜索使用了哪些類和成員,對於沒有使用的類和類成員,就會在壓縮階段丟棄。 接下來在Optimize階段,那些非Entry points的類、方法都會被設置爲private、static或者final,沒有使用的參數會被移除,此外,有些方法會被標記爲內聯。 在Obfuscate的步驟中,ProGuard會對非Entry points的類和方法進行重命名。bash
Includedescriptorclasses
:通常用於保證native方法名,確保方法的參數類型不會重命名,確保方法簽名不會被改變,這樣才能跟native libraries相匹配。Allowshrinking
:容許壓縮Allowoptimization
:容許優化Allowobfuscation
:容許混淆名稱Class Specifications是用來描述類和方法的模板,下面是這個模板的格式:app
其中,[]中的內容是可選,名稱可使用通配符,匹配構造函數、匹配成員、匹配方法,詳細請參考:proguard.sourceforge.net/manual/usag…ide
在配置文件中出現的相對路徑均是相對於該路徑,如圖: 函數
指定處理的jar包(或者aars, wars, ears, zips, apks, directories)等,這個jar包裏面的類將會被ProGuard處理並寫入到輸出的jar包裏去。通常非class文件會不作任何處理直接直接複製到輸出文件中,injars能夠屢次使用,引入不一樣的須要處理的文件。 注意,該選項能夠指定一個目錄,那麼該目錄下全部文件都會被看成input file處理。
設置處理完成後的輸出文件路徑
指定要處理應用程序的jar(或者aars, wars, ears, zips, apks, directories),這些文件不會包含到輸出文件中。通常是指被處理文件所依賴的一些jar包,而那些jar包是不須要被處理以及寫入到輸出文件的。好比:
忽略library裏面非public修飾的類。從而加快ProGuard的處理速度和下降ProGuard的使用內存。通常而言,library裏的非公開類是不能被程序使用的,忽略掉這些類能夠加快混淆速度。可是請注意,有一種特殊狀況:有些人編寫的代碼與類庫中的類在同一個包下,並且對該包的非public類進行了使用,在這種狀況下,就不能使用該選項了。
不忽略library裏面非public修飾的類
指定不忽略非public類裏面的成員和方法。ProGuard默認會忽略類庫裏非public類裏的成員和方法,可是因爲一些3.2.5裏面的一些緣由,應用程序裏可能會用到這些,這時候就須要這個選項來指定不忽略它們。
指定要保留在輸出文件內的目錄。默認狀況下,目錄會被移除。這會減小輸出文件的大小,但若是你的代碼引用到它們時可能會致使程序崩潰(如mypackage.MyCalss.class.getResource(""))。這時就須要指定-keepdirectories mypackage。-keepdirectories mydirectory匹配 mydirectory 目錄;-keepdirectories mydirectory/*匹配 mydirectory 的直接子目錄;-keepdirectorie mydirectory/**匹配全部子目錄,若是沒有指定過濾器,全部目錄會被保留。
指定被處理class文件所使用的java版本,可選的有: 1.0, 1.1, 1.2, 1.3, 1.4, 1.5 (or just 5), 1.6 (or just 6), 1.7 (or just 7), or 1.8 (or just 8).
強制輸出,即便輸出文件已是最新狀態
指定該類以及類的成員和方法爲entry points,不被ProGuard混淆
指定類的某些成員不被混淆,注意類名仍是會被混淆,如:
經過成員來指定哪些類不被混淆處理。好比能夠用來保留包含main方法的類:
若是指定了多條規則,以下,那麼必須同時包含sayHello和test兩個方法的類纔會被保留
-keepclassmembers,allowshrinking class_specification的別名,保留名稱不被混淆,但能夠被壓縮
-keepclasseswithmembers,allowshrinking class_specification的別名,保留名稱不被混淆,但能夠被壓縮
-keepclasseswithmembers,allowshrinking class_specification的別名
把keep匹配的類和方法輸出到文件中,能夠用來驗證本身設定的規則是否生效.
指定不進行壓縮.
把沒有使用的代碼輸出到文件中,方便查看哪些代碼被壓縮丟棄了。.
指定不對輸入代碼進行優化處理。優化選項是默認打開的。
指定混淆是採用的算法,後面的參數是一個過濾器,這個過濾器是谷歌推薦的算法,通常不作更改
指定優化的級別,在0-7之間,默認爲5.
能夠指定移除哪些方法沒有反作用,如在android開發中,如想在release版本能夠把全部log輸出都移除,能夠配置:
那麼全部log代碼將會在優化階段被去除。
指定不進行混淆
生成map文件,記錄混淆先後的名稱對應關係,注意,這個比較重要,由於混淆後運行的名稱會變得不可讀,只有依靠這個map文件來還原。
主要是用來維持兩次混淆公用一份mapping,確保相同的代碼先後兩次混淆後是同樣的命名
指定外部模糊字典
指定class模糊字典.
指定package模糊字典
類和成員混淆的時候,使用惟一的名字
不使用大小寫混合類名,注意,windows用戶必須爲ProGuard指定該選項,由於windows對文件的大小寫是不敏感的,也就是好比a.java和A.java會認爲是同一個文件。若是不這樣作而且你的項目中有超過26個類的話,那麼ProGuard就會默認混用大小寫文件名,致使class文件相互覆蓋。
保持packagename 不混淆
指定從新打包,全部包重命名,這個選項會進一步模糊包名,將包裏的類混淆成n個再從新打包到一個個的package中
將包裏的類混淆成n個再從新打包到一個統一的package中,會覆蓋flattenpackagehierarchy選項
混淆時可能被移除下面這些東西,若是想保留,須要用該選項,對於通常註解處理如 -keepattributes Annotation。
attribute_filter :
指定不執行預檢
把全部信息都輸出,而不只僅是輸出出錯信息
不輸出指定類的錯誤信息.
不打印指定類的警告信息
遇到警告的時候,忽略警告繼續執行ProGuard,不建議添加此項。
輸出當前ProGuard所使用的配置
指定輸出所處理的類的結構
在代碼中,若是用到了反射,混淆會改變類和成員的名字,致使反射找不到相應的類或者方法,因此開發者在混淆的時候,必須把用到了反射的類保留,不進行混淆。通常而言,使用反射通常會有如下方式,能夠搜索代碼,找到相關的類,而後在混淆配置裏面進行保留:
# reflectClass類使用了反射,保留該類
-keep class package.reflectClass { *; }
複製代碼
#代碼混淆壓縮比,在0~7之間,默認爲5,通常不作修改
-optimizationpasses 5
#指定混淆是採用的算法,後面的參數是一個過濾器,這個過濾器是谷歌推薦的算法,通常不作更改
-optimizations !code/simplification/cast,!field/*,!class/merging/*
#混合時不使用大小寫混合,混合後的類名爲小寫,windows下必須使用該選項
-dontusemixedcaseclassnames
#指定不去忽略非公共庫的類和成員
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
#輸出詳細信息
-verbose
#輸出類名->混淆後類名的映射關係
-printmapping map.txt
#不作預校驗,preverify是proguard的四個步驟之一,Android不須要preverify,去掉這一步可以加快混淆速度。
-dontpreverify
#保留Annotation不混淆
-keepattributes *Annotation*,InnerClasses
#避免混淆泛型
-keepattributes Signature
#拋出異常時保留代碼行號
-keepattributes SourceFile,LineNumberTable
#保留本地native方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
}
#保留枚舉類不被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
#保留Serializable序列化的類不被混淆
-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();
}
複製代碼
另外,因爲Android平臺在使用混淆的時候,還要特別注意要添加如下一些配置:
#保留咱們使用的四大組件,自定義的Application等等這些類不被混淆
-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.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View
-keep public class com.android.vending.licensing.ILicensingService
#保留support下的全部類及其內部類
-keep class android.support.** {*;}
#保留R下面的資源
-keep class **.R$* {*;}
#保留在Activity中的方法參數是view的方法,
-keepclassmembers class * extends android.app.Activity{
public void *(android.view.View);
}
#保留咱們自定義控件(繼承自View)不被混淆
-keep public class * extends android.view.View{
*** get*();
void set*(***);
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
#保留Parcelable序列化類不被混淆
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
#對於帶有回調函數的onXXEvent的,不能被混淆
-keepclassmembers class * {
void *(**On*Event);
}
#在咱們的app中使用了webView須要進行特殊處理
-keepclassmembers class * extends android.webkit.webViewClient {
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.webViewClient {
public void *(android.webkit.webView, jav.lang.String);
}
# 在app中與HTML5的JavaScript的交互進行特殊處理,如
# package com.ljd.example;
#
# public class JSInterface {
# @JavascriptInterface
# public void callAndroidMethod(){
# // do something
# }
# }
#咱們須要確保這些js要調用的原生方法不可以被混淆,因而咱們須要作以下處理
-keepclassmembers class com.ljd.example.JSInterface {
<methods>;
}
#內嵌類常常被混淆,結果在調用的時候就崩潰了,若是須要保留內嵌類,則用如下方法來保留內嵌類,如暴力MyClass裏面的內嵌類,$就是用來分割內嵌類和母體的標誌
-keep class com.test.MyClass$* {*;}
#-----------如下處理反射類---------------
複製代碼