Android混淆工具——Proguard實踐
最近使用了一個非常高效和方便的混淆工具—— Proguard ,使用了這個工具混淆打包后,apk體積顯著的減少了,而且反編譯難度也加大了,所以寫個博客記錄一下這個混淆的過程。
說說什么是 Proguard 吧。
Proguard 介紹
官網 的介紹是: ProGuard 是一個免費的 Java 類文件縮小,優化,混淆和預驗證的工具。它檢測和刪除未使用的類,字段,方法和屬性;優化字節碼并刪除未使用的指令;它使用短的無意義的名稱重命名剩余的類,字段和方法。所得到的應用程序和庫更小,更快,并且更好地針對逆向工程進行優化。
而且 Proguard 已經集成在 Android studio 構建系統里了,可以通過簡單的代碼來實現構建apk的時候進行混淆打包。
Proguard 使用
首先,我們需要在項目里的 build.gradle 文件里配置 Proguard 。
buildTypes {
release {
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
可以看見,分別在 release 和 debug 兩種構建類型配置了 Proguard ,在 debug 配置是因為你可以直接編譯測試你混淆的效果,是否有影響正常功能的使用。
shrinkResources 是去除無效的資源文件,壓縮資源。
minifyEnabled 是開啟混淆。
這是默認的 Proguard 配置, proguard-rules.pro 是需要在你的項目里創建的文件,層級跟 build.gradle 文件一樣的,當然你可以隨意改他的文件名,只不過需要在配置代碼里面跟著修改,其實這個文件就是 Proguard 的自定義配置文件, 沒有這個文件你構建是會報錯的 。
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
Proguard 基本語法
-keep 不混淆類和成員或者被重命名
-keepnames 防止類和成員被重命名
-keepclassmembers 不混淆成員被移除或者被重命名
-keepnames 防止成員被重命名
-keepclasseswithmembers 不混淆擁有該成員的類和成員或者被重命名
-keepclasseswithmembernames 防止擁有該成員的類和成員被重命名
而且還支持通配符*
例如
不混淆某個類
-keep public class com.shadow.example.abc { *; }
不混淆某個包
-keep public class com.shadow.example.** { *; }
不混淆某個類的子類
-keep public class * extends com.shadow.exampl.abc { *; }
了解這些基本語法后,我們就來看一下自定義配置,沒有自定義混淆配置的話會構建出錯的。
Proguard 自定義配置
接下來介紹一下自定義混淆配置
以下是我在項目里使用的配置,我會在注釋里說明。
//--- 基礎混淆配置 ---
-optimizationpasses 5 //指定代碼的壓縮級別
-allowaccessmodification //優化時允許訪問并修改有修飾符的類和類的成員
-dontusemixedcaseclassnames //不使用大小寫混合
-dontskipnonpubliclibraryclasses //指定不去忽略非公共庫的類
-verbose //混淆時是否記錄日志
-ignorewarnings //忽略警告,避免打包時某些警告出現,沒有這個的話,構建報錯
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* //混淆時所采用的算法
-keepattributes *Annotation* //不混淆注解相關
-keepclasseswithmembernames class * { //保持 native 方法不被混淆
native <methods>;
}
-keepclassmembers enum * { //保持枚舉 enum 類不被混淆
public static **[] values();
public static ** valueOf(java.lang.String);
}
//不混淆Parcelable
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
//不混淆Serializable
-keep class * implements java.io.Serializable {*;}
-keepnames class * implements java.io.Serializable
-keepclassmembers class * implements java.io.Serializable {*;}
-keepclassmembers class **.R$* { //不混淆R文件
public static <fields>;
}
//不做預校驗,preverify是proguard的四個步驟之一,Android不需要preverify,去掉這一步能夠加快混淆速度。
-dontpreverify
-keepattributes Signature //過濾泛型 出現類型轉換錯誤時,啟用這個
//--- 不能被混淆的基類 ---
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Fragment
-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 class org.xmlpull.v1.** { *; }
//--- 不混淆android-support-v4包 ---
-dontwarn android.support.v4.**
-keep class android.support.v4.** { *; }
-keep interface android.support.v4.app.** { *; }
-keep class * extends android.support.v4.** { *; }
-keep public class * extends android.support.v4.**
-keep public class * extends android.support.v4.widget
-keep class * extends android.support.v4.app.** {*;}
-keep class * extends android.support.v4.view.** {*;}
-keep public class * extends android.support.v4.app.Fragment
//不混淆繼承的support類
-keep public class * extends android.support.v4.**
-keep public class * extends android.support.v7.**
-keep public class * extends android.support.annotation.**
//不混淆log
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(java.lang.String, int);
public static int v(...);
public static int i(...);
public static int w(...);
public static int d(...);
public static int e(...);
}
//保持Activity中參數類型為View的所有方法
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
//--- 不混淆第三方庫 這個可以去相關的第三方庫官網找尋混淆代碼 如果被混淆了會無法使用 ---
//Gson
-keepattributes *Annotation*
-keep class sun.misc.Unsafe { *; }
-keep class com.idea.fifaalarmclock.entity.***
-keep class com.google.gson.stream.** { *; }
-keep class com.你的bean.** { *; }
//OkHttp3
-dontwarn okhttp3.logging.**
-keep class okhttp3.internal.**{*;}
-dontwarn okio.**
//Retrofit
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes Signature
-keepattributes Exceptions
//RxJava RxAndroid
-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
long producerIndex;
long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
rx.internal.util.atomic.LinkedQueueNode producerNode;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
rx.internal.util.atomic.LinkedQueueNode consumerNode;
}
//微信
-keep class com.tencent.mm.** {*;}
//Glide圖片庫
-keep class com.bumptech.glide.**{*;}
//友盟
-keepclassmembers class * {
public <init> (org.json.JSONObject);
}
-keep class com.umeng.onlineconfig.OnlineConfigAgent {
public <fields>;
public <methods>;
}
-keep class com.umeng.onlineconfig.OnlineConfigLog {
public <fields>;
public <methods>;
}
-keep interface com.umeng.onlineconfig.UmengOnlineConfigureListener {
public <methods>;
}
//Testin
-dontwarn com.testin.agent.**
-keep class com.testin.agent.** {*;}
//--- 一些特殊的混淆配置 ---
//有用到WEBView的JS調用接口不被混淆
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
public *;
}
//對于帶有回調函數的onXXEvent、**On*Listener的,不能被混淆
-keepclassmembers class * {
void *(**On*Event);
void *(**On*Listener);
}
//拋出異常時保留代碼行號 方便測試
-keepattributes SourceFile,LineNumberTable
//不混淆我們自定義控件(繼承自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);
}
你用了以上的配置文件,我相信大部分項目都是沒有問題的
在初次使用 Proguard 實現混淆配置的時候,會出現很多很多的坑,如果遇上了問題,可以通過報錯信息去谷歌一下,有可能會有很多你沒想到遺漏,一開始我也是踩了好多坑。。
不過,通過努力還是可以完成這個混淆實踐的,混淆后的 apk 體積會讓你驚訝的!
值得試試
最后
這是一個簡單的 Proguard 混淆實踐,記錄一下思路。
來自:http://www.jianshu.com/p/ea223e0f3737