前言
最近在整理項目中的混淆,踩了很多坑走趋,如果不打開混淆衅金,項目上線了等于裸奔,風(fēng)險很大簿煌,混淆如果打開了處理不好氮唯,會出現(xiàn)很多莫名其妙的問題,所以我整理了比較全面的代碼混淆方法姨伟,包括組件化的代碼混淆方案惩琉,比較實用,希望對大家有幫助夺荒。
開啟混淆
打開app模塊下的build.gradle
文件瞒渠,把minifyEnabled
設(shè)置為true,代碼如下
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
proguard-android.txt
是Android提供的默認(rèn)混淆配置文件技扼,在配置的Android sdk /tools/proguard目錄下伍玖,感興趣的可以打開看下,proguard-rules.pro
是我們自定義的混淆配置文件,我們可以將我們自定義的混淆規(guī)則放在里面剿吻。
自定義混淆規(guī)則
混淆常見命令
命令 | 簡介 |
---|---|
dontwarn | dontwarn基本會和keep同時出現(xiàn)窍箍,尤其是在引入library的時候,是為了忽略library的警告丽旅,保證build的正常進(jìn)行 |
keep | 保留類和類中的成員椰棘,防止被重命名或移除 |
keepnames | 保留類和類中的成員,防止被重命名魔招,成員沒有被引用會被移除 |
keepclassmembers | 只保留類中的成員晰搀,防止被重命名或移除 |
keepclassmembernames | 只保留類中的成員,防止被重命名办斑,成員沒有引用會被移除 |
keepclasseswithmembers | 保留擁有該成員的類和成員外恕,防止被重命名或移除 |
keepclasseswithmembernames | 保留擁有該成員的類和成員杆逗,防止被重命名 |
keep的規(guī)則
[keep命令] [類] {
[成員]
}
-
“類”代表類相關(guān)的限定條件,它將最終定位到某些符合該限定條件的類鳞疲。它的內(nèi)容可以使用:
- 具體的類
- 訪問修飾符(public罪郊、protected、private)
- 通配符*尚洽,匹配任意長度字符悔橄,但不含包名分隔符(.)
- 通配符**,匹配任意長度字符腺毫,并且包含包名分隔符(.)
- extends癣疟,即可以指定類的基類
- implement,匹配實現(xiàn)了某接口的類
- $潮酒,內(nèi)部類
-
“成員”代表類成員相關(guān)的限定條件睛挚,它將最終定位到某些符合該限定條件的類成員。它的內(nèi)容可以使用:
- <init> 匹配所有構(gòu)造器
- <fields> 匹配所有域
- <methods> 匹配所有方法
- 通配符*急黎,匹配任意長度字符扎狱,但不含包名分隔符(.)
- 通配符**,匹配任意長度字符勃教,并且包含包名分隔符(.)
- 通配符***淤击,匹配任意參數(shù)類型
- …,匹配任意長度的任意類型參數(shù)故源。比如void test(…)就能匹配任意 void test(String a) 或者是 void test(int a, String b) 這些方法污抬。
- 訪問修飾符(public、protected绳军、private)
常用混淆規(guī)則
#關(guān)閉bugly sdk的警告
-dontwarn com.tencent.bugly.**
#不混淆某個類
-keep public class com.jesse.example.Test { *; }
#不混淆某個類的子類
-keep public class * extends com.jesse.example.Test { *; }
#不混淆某個包所有的類
-keep class com.jesse.example.bean.** { *; }
#不混淆所有類名中包含了“model”的類及其成員
-keep public class **.*model*.** {*;}
#不混淆某個接口的實現(xiàn)
-keep class * implements com.jesse.example.TestInterface { *; }
#不混淆某個類的構(gòu)造方法
-keepclassmembers class com.jesse.example.Test {
public <init>();
}
#不混淆某個類的特定的方法
-keepclassmembers class com.jesse.example.Test {
public void test(java.lang.String);
}
#不混淆某個類的內(nèi)部類
-keep class com.jesse.example.Test$* {
*;
}
更多proguard規(guī)則壕吹,可以去官網(wǎng)查看
組件化代碼混淆方案
首先我們在創(chuàng)建一個Android Module的時候,在Module的build.gradle
中都會自動生成兩個proguard的配置删铃,如下:
defaultConfig {
...
consumerProguardFiles 'consumer-rules.pro'
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
關(guān)于consumerProguardFiles
和proguardFiles
的區(qū)別,網(wǎng)上查了下踏堡,沒有一個說的清楚點的猎唁,求人不如求己,在經(jīng)過我的實踐之后顷蟆,我總結(jié)了有以下幾個區(qū)別诫隅,感興趣的也可以自己實踐下,看看我說的對不對:
-
consumerProguardFiles
配置的proguard會被打進(jìn)aar包中帐偎,而proguardFiles
配置的proguard不會被打進(jìn)aar中 -
proguardFiles
配置的proguard文件只作用于庫文件代碼逐纬,只在編譯發(fā)布aar的時候有效,在你將庫文件作為一個模塊添加到App模塊中后削樊,庫文件中consumerProguardFiles
配置的proguard文件則會追加到app模塊的Proguard配置文件中豁生,作用于整個app代碼兔毒。
了解了他們的區(qū)別后,我們來看組件化代碼混淆方案甸箱。
方案一:在app模塊中管理所有的混淆規(guī)則
優(yōu)點:所有混淆規(guī)則在app模塊的proguard-rule.pro
文件中統(tǒng)一管理
缺點:移除某些模塊后育叁,需手動移除app模塊中的混淆規(guī)則。理論上混淆規(guī)則添加多了不會造成崩潰或者編譯不通過芍殖,但是會影響編譯效率
方案二:組件模塊管理各自的混淆規(guī)則
優(yōu)點:將混淆文件解耦到每個模塊中豪嗽,并且不會影響編譯效率
那么我們應(yīng)該如何解耦呢?我們可以通過consumerProguardFiles
在各個組件模塊中配置各自的混淆規(guī)則豌骏,因為這種方式配置的混淆規(guī)則最終都會追加到app模塊的混淆規(guī)則中龟梦,并最終統(tǒng)一混淆。
組件化代碼混淆總結(jié)
我們可以將固定的第三方混淆放到common模塊的consumer-rules.pro
文件中窃躲,每個模塊獨(dú)有的第三方引用庫混淆放到各自的consumer-rules.pro
文件中计贰,在app模塊的proguard-rule.pro
文件中放入Android通用的混淆聲明,如四大組件和全局的混淆等配置框舔。這樣可以最大限度的完成混淆解耦操作蹦玫。
最后附上app中通用的混淆配置
#---------------------------------基本指令區(qū)----------------------------------
# 指定代碼的壓縮級別 0 - 7(指定代碼進(jìn)行迭代優(yōu)化的次數(shù),在Android里面默認(rèn)是5刘绣,這條指令也只有在可以優(yōu)化時起作用樱溉。)
-optimizationpasses 5
# 混淆時不會產(chǎn)生形形色色的類名(混淆時不使用大小寫混合類名)
-dontusemixedcaseclassnames
# 指定不去忽略非公共的庫類(不跳過library中的非public的類)
-dontskipnonpubliclibraryclasses
# 指定不去忽略非公共的的庫類的成員
-dontskipnonpubliclibraryclassmembers
#不進(jìn)行預(yù)校驗,Android不需要,可加快混淆速度。
-dontpreverify
# 混淆時記錄日志(打印混淆的詳細(xì)信息)
# 這句話能夠使我們的項目混淆后產(chǎn)生映射文件
# 包含有類名->混淆后類名的映射關(guān)系
-verbose
-printmapping proguardMapping.txt
# 指定混淆是采用的算法纬凤,后面的參數(shù)是一個過濾器
# 這個過濾器是谷歌推薦的算法福贞,一般不做更改
-optimizations !code/simplification/cast,!field/*,!class/merging/*
#保護(hù)代碼中的 Annotation 內(nèi)部類不被混淆
-keepattributes *Annotation*,InnerClasses
-ignorewarning
# 避免混淆泛型,這在 JSON 實體映射時非常重要停士,比如 fastJson
-keepattributes Signature
# 拋出異常時保留代碼行號挖帘,在異常分析中可以方便定位
-keepattributes SourceFile,LineNumberTable
#----------------------------------------------------------------------------
#---------------------------------默認(rèn)保留區(qū)---------------------------------
-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
-keep class android.support.** {*;}
# 保留所有的本地 native 方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
}
# 保留在 Activity 中的方法參數(shù)是 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{
*** 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);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
# 保留 Parcelable 序列化的類不被混淆
-keep class * implements android.os.Parcelable {
*;
}
# 保留 Serializable 序列化的類不被混淆
-keep class * implements java.io.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();
}
# 對于 R(資源)下的所有類及其方法恋技,都不能被混淆
-keep class **.R$* {
*;
}
# 對于帶有回調(diào)函數(shù) onXXEvent 的拇舀,不能被混淆
-keepclassmembers class * {
void *(**On*Event);
}
-keepclassmembers class * {
public <init>(org.json.JSONObject);
}
-keepattributes *JavascriptInterface*
#----------------------------------------------------------------------------
#---------------------------------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);
}