Android混淆詳解
ProGuard簡介
在最新的Android Studio 2.2.2版本創建的Android工程中,module中的build.gradle有如下一段配置。這里的minifyEnabled即用來控制在編譯時是否需要啟用Proguard,將minifyEnabled修改為true,即表示啟用Proguard。’proguard-android.txt’是Android SDK中自帶的一個基本Progurad配置文件,默認是空白的,需要由開發者自行添加哪些需要混淆哪些不混淆,形如:
-ignorewarning # 是否忽略檢測,(是)
-optimizationpasses 5 # 指定代碼的壓縮級別
-dontusemixedcaseclassnames # 是否使用大小寫混合
-dontpreverify # 混淆時是否做預校驗
-verbose # 混淆時是否記錄日志
-optimizations !code/simplification/arithmetic,!field/,!class/merging/ # 混淆時所采用的算法
-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 com.android.vending.licensing.ILicensingService # 保持哪些類不被混淆
-keepattributes Annotation #保持注解
-keep public class extends android.widget.BaseAdapter {;}
-keepclasseswithmembernames class * { # 保持 native 方法不被混淆
native <methods>;
}</code></pre>
在Android中一提起ProGuard,我們就會認為他是用來混淆代碼的,殊不知ProGuard一共包括以下4步。
- 壓縮(Shrink):偵測并移除代碼中無用的類、字段、方法、和特性(Attribute)。
- 優化(OPtimize):對字節碼進行優化,移除無用指令。
- 混淆(Obfuscate):使用a、b、c、d這樣簡短而無意義的名稱,對類、字段和方法進行重命名。
- 預檢(Preveirfy): 在java平臺上對處理后的代碼進行預檢。
說到這里我們需要對Android打包的原理有一個簡單的了解,首先來看一下在Proguard幫助文檔中給出了一個Proguard工作流程圖

Proguard按如下流程進行打包:
Input jars、Library jars-shrink->Shrunk code-optimize->Optim.code-obfuscate->Obfusc.code-preverify->Output >jars、Library jars
Proguard使用library jars來輔助對input jars類之間的依賴關系進行解析, library jars自身不會被處理,也不會被包含到output jars中。
這里我們引入Entry Point的概念。Entry Point是在ProGuard過程中不會被處理的類或方法。再壓縮的步驟中,ProGuard或從上述的EntryPoint開始遞歸遍歷,搜索那些類和類成員在使用。對于沒有被使用的類和類的成員,就會在壓縮階段丟棄。
接下來優化的步驟中,那些非EntryPoint的類、方法都會被設置為private、static或final,不使用的參數會被移除,此外,有些方法會被標記為內聯的。在混淆的步驟中,ProGuard會對非EntryPoint的類和方法進行重命名。
Proguard使用
Proguard工具目錄結構

lib目錄
lib目錄中包含了Proguard工具對應的jar文件,其中又包含三個文件:proguard.jar,proguardgui.jar和retrace.jar。
Proguard四項核心功能shrink,optimize,obfuscate和preverify的執行都是由proguard.jar來完成的,不過proguard.jar只能通過命令行方式來使用。
proguardgui.jar是Proguard提供的一個圖形界面工具,通過proguardgui.jar可以方便的查看和編輯Proguard配置,以及調用proguard.jar來執行一次優化過程。
retrace.jar主要在debug時使用。混淆之后的jar文件執行過程如果出現異常,生成的異常信息將很難被解讀,方法調用的堆棧都是一些混淆之后的名字,通過retrace.jar可以將異常的堆棧信息中的方法名還原成混淆前的名字,方便程序解決bug。
bin目錄
bin目錄中包含了幾個bat和shell腳本,通過這些腳本可以直接執行proguard.jar,proguardgui.jar和retrace.jar。如果將bin目錄添加到環境變量中,就可以直接在命令行中執行proguard,proguardgui和retrace命令了,避免每次都要輸入java -jar +
proguard.jar的使用
使用proguard.jar有幾種方式:
1,通過命令行執行”java -jar +
java -jar proguard.jar -injars myapp. jar -outjars myapp_out.jar -libraryjars 'D:\android-sdk\platforms\android-23\android.jar' // 只使用配置選項
java -jar proguard.jar @myconfig.pro // 只使用配置文件
java -jar proguard.jar @myconfig.pro -verbose // 混合使用配置文件和配置選項
proguardgui.jar的使用
使用proguardgui.jar有幾種方式:
1,通過命令行執行”java -jar +
java -jar proguardgui.jar // 不使用配置文件
java -jar proguardgui.jar @myconfig.pro // 使用配置文件
retrace.jar的使用
使用retrace.jar有幾種方式:
1,通過命令行執行”java -jar +
java -jar retrace.jar mapping_file
java -jar retrace.jar mapping_file exception_statck_file.txt
java -jar retrace.jar -verbose mapping_file exception_statck_file.txt
如何寫一個ProGuard文件
如何寫一個ProGuard文件呢?主要有三步驟:
基本混淆
# 代碼混淆壓縮比,在0~7之間,默認為5,一般不下需要修改
-optimizationpasses 5
混淆時不使用大小寫混合,混淆后的類名為小寫
windows下的同學還是加入這個選項吧(windows大小寫不敏感)
-dontusemixedcaseclassnames
指定不去忽略非公共的庫的類
默認跳過,有些情況下編寫的代碼與類庫中的類在同一個包下,并且持有包中內容的引用,此時就需要加入此條聲明
-dontskipnonpubliclibraryclasses
指定不去忽略非公共的庫的類的成員
-dontskipnonpubliclibraryclassmembers
不做預檢驗,preverify是proguard的四個步驟之一
Android不需要preverify,去掉這一步可以加快混淆速度
-dontpreverify
有了verbose這句話,混淆后就會生成映射文件
包含有類名->混淆后類名的映射關系
然后使用printmapping指定映射文件的名稱
-verbose
-printmapping priguardMapping.txt
指定混淆時采用的算法,后面的參數是一個過濾器
這個過濾器是谷歌推薦的算法,一般不改變
-optimizations !code/simplification/artithmetic,!field/,!class/merging/
保護代碼中的Annotation不被混淆
這在JSON實體映射時非常重要,比如fastJson
-keepattributes Annotation
避免混淆泛型
這在JSON實體映射時非常重要,比如fastJson
-keepattributes Signature
拋出異常時保留代碼行號
-keepattributes SourceFile,LineNumberTable</code></pre>
不混淆,需要保留的東西
# 保留所有的本地native方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
}
保留了繼承自Activity、Application這些類的子類
因為這些子類有可能被外部調用
比如第一行就保證了所有Activity的子類不要被混淆
-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
如果有引用android-support-v4.jar包,可以添加下面這行
-keep public class com.null.test.ui.fragment.* {;}
保留Activity中的方法參數是view的方法,
從而我們在layout里面編寫onClick就不會影響
-keepclassmembers class extends android.app.Activity {
public void (android.view.View);
}
枚舉類不能被混淆
-keepclassmembers enum * {
public static [] values();
public static valueOf(java.lang.String);
}
保留自定義控件(繼承自View)不能被混淆
-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();
get* ();
}
保留Parcelable序列化的類不能被混淆
-keep class implements android.os.Parcelable{
public static final android.os.Parcelable$Creator ;
}
保留Serializable 序列化的類不被混淆
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <fields>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
對R文件下的所有類及其方法,都不能被混淆
-keepclassmembers class *.R$ {
*;
}
對于帶有回調函數onXXEvent的,不能混淆
-keepclassmembers class {
void (On*Event);
}</code></pre>
一般第三方和自己的bean文件是不需要混淆的。
-keep class com.null.test.entities.
{
//全部忽略
;
}
-keep class com.null.test.entities.** {
//忽略get和set方法
public void set();
public get();
public ** is();
}
//以上兩種任意一種都行</code></pre>
內嵌類
-keep class com.null.test.MainActivity$
{
;
}</code></pre>
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, java.lang.String);
}</code></pre>
第三方庫
-libraryjars ./libs/android-support-v4.jar
-dontwarn android.support.v4.**
-dontwarn **CompatHoneycomb
-dontwarn **CompatHoneycombMR2
-dontwarn **CompatCreatorHoneycombMR2
-keep interface android.support.v4.app.** { *; }
-keep class android.support.v4.** { *; }
-keep public class * extends android.support.v4.**
-keep public class * extends android.app.Fragment
混淆注意事項
- 混淆必須對項目不造成任何崩潰問題。
- 打包時忽略警告
當在導出時,發現很多could not reference class之類的warning信息,如果確認app運行中和那些引用沒有什么關系的話,就可以添加-dontwarn標簽,就不會在提示這些warning信息了。如-dontwarn org.apache.**。
- 使用annotation避免混淆
@Keep
@KeepPublicGettersSetters
public class Bean {
public boolean booleanProperty;
public int intProperty;
public String stringProperty;
public boolean isBooleanProperty() {
return booleanProperty;
}
}
注:android studio 是在build.gradle修改buildTypes如下:
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
來自:http://blog.csdn.net/xiangzhihong8/article/details/53790660