做爲Java和Android開發者,你們應該都對混淆很熟悉了。網上也有各路大神提供的混淆模板,基本上直接拿來用就好。但我仍是想捋一捋,由於工做中被混淆這傢伙「玩弄」了好幾回,必須把它記在小本本上。javascript
混淆,字面上來講就是把項目中的包名、類名、方法名和變量名等進行更改,用以迷惑別人。但混淆其實包含了代碼壓縮、優化、校驗等過程,把混淆稱做ProGuard
更合適。html
PS:ProGuard官網
stuff.mit.edu/afs/sipb/pr…java
ProGuard就是Java對Class文件進行「混淆」的工具。直接貼圖吧:linux
(看到這個圖,你們有沒有想到什麼?——設計模式中的 責任鏈模式。)這四個步驟其實都是可選的。固然,Android中通常狀況下咱們都會保留前三個步驟,而忽略preverify
過程,這樣能夠加快混淆速度。android
Android中默認集成了ProGuard工具,在sdk目錄的/tools/proguard中。混淆開啓方法:web
android {
...
// AS自動生成
buildTypes {
release {
// 混淆開關
minifyEnabled true
// 移除無用的resource文件
shrinkResources true
// proguard-android.txt表示默認的混淆規則
// proguard-rules.pro表示自定義的混淆規則(文件名和後綴能夠修改)
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } 複製代碼
默認的proguard-android.txt
文件在sdk目錄/tools/proguard中。該目錄下還有個proguard-android-optimize.txt
文件。而咱們自定義的proguard-rules.pro
文件中有部分基礎混淆規則就是來自proguard-android-optimize.txt
。算法
混淆的語法均可以在上述的ProGuard官網
中找到,這裏只介紹一些經常使用的語法規則。設計模式
一、保留類和類成員安全
保留 | 反之被刪除或重命名 | 防止被重命名 |
---|---|---|
類和類成員 | -keep | -keepnames |
僅類成員 | -keepclassmembers | -keepclassmembernames |
若是擁有某成員,保留類和類成員 | -keepclasseswithmembers | -keepclasseswithmembernames |
二、類成員中的一些符號bash
符號 | 做用 |
---|---|
<init> | 匹配全部構造器 |
<fields> | 匹配全部域 |
<methods> | 匹配全部方法 |
* | 匹配全部域和方法 |
三、一些經常使用通配符
通配符 | 做用 |
---|---|
* | 匹配任意長度字符,但不含包名分隔符(.) |
** | 匹配任意長度字符,而且包含包名分隔符(.) |
*** | 匹配任意參數類型 |
... | 匹配任意長度的任意類型參數 |
% | 匹配任何原始類型 |
? | 匹配類名中的任何單個字符 |
其餘的一些語法基本上屬於無變化的那種,在混淆規則中基本都固定使用了,有須要的話能夠自行在官方查詢。
嗯...這模板基本都是大同小異的,這裏就直接貼出經常使用的混淆規則了。
# 優化算法,通常不用修改,來自Google
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
# 代碼混淆壓縮比,在0~7之間,默認爲5,通常不作修改
-optimizationpasses 5
# 混合時不使用大小寫混合,混合後的類名爲小寫
-dontusemixedcaseclassnames
# 不去忽略非公共庫的類
-dontskipnonpubliclibraryclasses
# 優化時容許訪問並修改有修飾符的類和類的成員
-allowaccessmodification
# 項目混淆後產生映射文件
-verbose
# 不作預校驗
-dontpreverify
# 保留Annotation不混淆
-keepattributes *Annotation*,InnerClasses
# 避免混淆泛型
-keepattributes Signature
# 拋出異常時保留代碼行號
-keepattributes SourceFile,LineNumberTable
# 保留四大組件,自定義的Application等
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Appliction
-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.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService
# 保留native方法
-keepclasseswithmembernames class * {
native <methods>;
}
# 保留在Activity中的方法參數是view的方法,
# 這樣以來咱們在layout中寫的onClick就不會被影響
-keepclassmembers class * extends android.app.Activity{
public void *(android.view.View);
}
# 保留自定義View
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
# 保留枚舉
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# 保留Parcelable序列化對象
-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}
# 保留R文件中的成員
-keepclassmembers class **.R$* {
public static <fields>;
}
# 保留Serializable序列化的類不被混淆
-keepnames class * implements java.io.Serializable
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <fields>;
!private <fields>;
!private <methods>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# -------------------------------------------------------------------------------
# webView處理,項目中沒有使用到webView忽略便可
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
public *;
}
-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);
}
# --------------------------保留JS接口-------------------------------------------
# --------------------------保留反射類-------------------------------------------
# --------------------------保留實體類-------------------------------------------
# --------------------------第三方庫的混淆規則-----------------------------------
複製代碼
以上都是一些經常使用的混淆規則,其中部分規則其實都在Android默認混淆文件中聲明過了。因此具體的自定義混淆規則仍是得根據項目進行變化。
混淆後通常都有下面幾個文件:
遇到混淆問題時,我一般是經過查看mapping.txt文件來分析緣由的。
若是在進行Crash追蹤中遇到了困難,可使用sdk目錄下/tools/proguard/bin中的proguardgui.bat
可視化工具進行混淆定位。示例如圖:
場景一
問題描述:業務方集成了咱們的一個SDK,SDK的一個頁面中會顯示Banner圖,而Banner圖所用的是SDK中自定義的ViewPager組件,結果業務方那邊沒法顯示Banner圖。
緣由分析:經過反編譯apk,比對mapping.txt文件,發現Banner圖的自定義Adapter中關鍵方法都被清除了。查看混淆文件,發現只keep了support v4的ViewPager,並無keep住PagerAdapter。而SDK中又是經過compileOnly(provided)方式依賴v4包的,致使自定義的Adapter在工程編譯的時候找不到被引用的關係,而後就被混淆給優化掉了。
經驗:針對compileOnly(provided)依賴的庫,必定要注意相關類的混淆,特別是SDK開發者,由於混淆致使的問題通常較難定位。
場景二
問題描述:業務方又集成了咱們的一個SDK(沒錯,是「又」),SDK有一個換膚功能,業務方能夠經過構造特定格式的資源文件來實現皮膚替換。但在業務經過公司編譯工具編譯後的apk中沒法實現換膚功能。
緣由分析:機智的我很快想到了混淆問題,因而讓業務方加上保留全部R文件的規則,結果仍是失敗。因而開始了長時間的打log看log過程,仍是肯定問題出在資源名的混淆上。最後發現業務方雖然在本地編譯環境中keep了資源名,但在公司的編譯環境中仍是對資源進行了混淆,致使換膚失敗。
經驗:對於和資源相關的功能,必定要注意R文件的混淆。不要輕易相信業務,由於他們常常有你預料不到的操做(手動滑稽...)。
工做中遇到過不少次混淆相關的問題,這裏就很少說了,只要你們細心點就行了。