Android SDK自帶了混淆工具Proguard
。它位於SDK根目錄\tools\proguard
下面。 ProGuard
是一個免費的Java類文件收縮,優化,混淆和預校驗器。它能夠檢測並刪除未使用的類,字段,方法和屬性。它能夠優化字節碼,並刪除未使用的指令。它能夠將類、字段和方法使用短無心義的名稱進行重命名。最後,預校驗的Java6或針對Java MicroEdition的所述處理後的碼。 若是開啓了混淆,Proguard
默認狀況下會對全部代碼,包括第三方包都進行混淆,但是有些代碼或者第三方包是不能混淆的,這就須要咱們手動編寫混淆規則來保持不能被混淆的部分。java
Android中的「混淆」能夠分爲兩部分,一部分是 Java 代碼的優化與混淆,依靠 proguard
混淆器來實現;另外一部分是資源壓縮,將移除項目及依賴的庫中未被使用的資源(資源壓縮嚴格意義上跟混淆沒啥關係,但通常咱們都會放一塊兒講)。android
壓縮(Shrinking):默認開啓,用以減少應用體積,移除未被使用的類和成員,而且會在優化動做執行以後再次執行(由於優化後可能會再次暴露一些未被使用的類和成員)。web
-dontshrink 關閉壓縮
複製代碼
優化(Optimization):默認開啓,在字節碼級別執行優化,讓應用運行的更快。算法
-dontoptimize 關閉優化
-optimizationpasses n 表示proguard對代碼進行迭代優化的次數,Android通常爲5
複製代碼
混淆(Obfuscation):默認開啓,增大反編譯難度,類、函數、變量名會被隨機命名成無心義的代號形如:a,b,c...之類的,除非用keep保護。windows
-dontobfuscate 關閉混淆
複製代碼
上面這幾個功能都是默認打開的,要關閉他們只需配置對應的規則便可。 混淆後默認會在工程目錄app/build/outputs/mapping/release
下生成一個mapping.txt
文件,這就是混淆規則,咱們能夠根據這個文件把混淆後的代碼反推回源本的代碼,因此這個文件很重要,注意保護好。原則上,代碼混淆後越亂越無規律越好,但有些地方咱們是要避免混淆的,不然程序運行就會出錯。bash
資源壓縮將移除項目及依賴的庫中未被使用的資源,這在減小 apk 包體積上會有不錯的效果,通常建議開啓。具體作法是在 build.grade
文件中,將 shrinkResources
屬性設置爲 true
。須要注意的是,只有在用minifyEnabled true
開啓了代碼壓縮後,資源壓縮纔會生效。 資源壓縮包含了「合併資源」和「移除資源」兩個流程。 「合併資源」流程中,名稱相同的資源被視爲重複資源會被合併。須要注意的是,這一流程不受shrinkResources
屬性控制,也沒法被禁止, gradle 必然會作這項工做,由於假如不一樣項目中存在相同名稱的資源將致使錯誤。gradle 在四處地方尋找重複資源:markdown
src/main/res/
路徑依賴 -> main -> 渠道 -> 構建類型
複製代碼
舉個例子,假如重複資源同時存在於main
文件夾和不一樣渠道中,gradle 會選擇保留渠道中的資源。 同時,若是重複資源在同一層次出現,好比src/main/res/
和 src/main/res2/
,則 gradle
沒法完成資源合併,這時會報資源合併錯誤。 「移除資源」流程則見名知意,須要注意的是,相似代碼,混淆資源移除也能夠定義哪些資源須要被保留,這點在下文給出。網絡
命令 | 做用 |
---|---|
-keep | 保持類和類成員,防止被移除或者被重命名 |
-keepnames | 保持類和類成員,防止被重命名 |
-keepclassmembers | 保持類成員,防止被移除或者被重命名 |
-keepclassmembernames | 保持類成員,防止被重命名 |
-keepclasseswithmembers | 保持擁有該成員的類和成員,防止被移除或者被重命名 |
-keepclasseswithmembernames | 保持擁有該成員的類和成員,防止被重命名 |
保持元素不參與混淆的規則的命令格式:app
[保持命令] [類] {
[成員]
}
複製代碼
「類」表明類相關的限定條件,它將最終定位到某些符合該限定條件的類。它的內容可使用:ide
public、protected、private
)*
,匹配任意長度字符,但不含包名分隔符(.)**
,匹配任意長度字符,而且包含包名分隔符(.)extends
,便可以指定類的基類implement
,匹配實現了某接口的類$
,內部類 「成員」表明類成員相關的限定條件,它將最終定位到某些符合該限定條件的類成員。它的內容可使用:<init>
匹配全部構造器<fields>
匹配全部域<methods>
匹配全部方法*
,匹配任意長度字符,但不含包名分隔符(.)**
,匹配任意長度字符,而且包含包名分隔符(.)***
,匹配任意參數類型…
,匹配任意長度的任意類型參數。好比void test(…)就能匹配任意 void test(String a) 或者是 void test(int a, String b) 這些方法。public、protected、private
)-keep public class com.android.proguard.example.Test { *; }
複製代碼
-keep class com.android.proguard.example.** { *; }
複製代碼
-keep public class * extends com.android.proguard.example.Test { *; }
複製代碼
-keep public class **.*model*.** {*;}
複製代碼
-keep class * implements com.android.proguard.example.TestInterface { *; }
複製代碼
-keepclassmembers class com.android.proguard.example.Test {
public <init>();
}
複製代碼
-keepclassmembers class com.android.proguard.example.Test { public void test(java.lang.String); } 複製代碼
-keep class com.android.proguard.example.Test$* {
*;
}
複製代碼
-keep public class * extends android.app.Fragment
-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.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService
複製代碼
-dontwarn android.support.**
-keep public class * extends android.support.v4.**
-keep public class * extends android.support.v7.**
-keep public class * extends android.support.annotation.**
複製代碼
-keepclasseswithmembernames class * { #### native <methods>; } 複製代碼
-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);
}
複製代碼
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
複製代碼
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
複製代碼
-keep class **.R$* { *; }
複製代碼
-keep class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator *; } 複製代碼
-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(); } 複製代碼
-keep public class * extends android.widget.BaseAdapter { *; }
複製代碼
-keep public class * extends android.widget.CusorAdapter{ *; }
複製代碼
用shrinkResources true
開啓資源壓縮後,全部未被使用的資源默認被移除。假如你須要定義哪些資源必須被保留,在res/raw/
路徑下建立一個xml文件,例如keep.xml
。 經過一些屬性的設置能夠實現定義資源保持的需求,可配置的屬性有:
tools:keep
定義哪些資源須要被保留(資源之間用「,」隔開)tools:discard
定義哪些資源須要被移除(資源之間用「,」隔開)tools:shrinkMode
開啓嚴格模式 當代碼中經過 Resources.getIdentifier()
用動態的字符串來獲取並使用資源時,普通的資源引用檢查就可能會有問題。例如,以下代碼會致使全部以「img_」開頭的資源都被標記爲已使用。String name = String.format("img_%1d", angle + 1); res = getResources().getIdentifier(name, "drawable", getPackageName()); 複製代碼
咱們能夠設置 tools:shrinkMode
爲 strict
來開啓嚴格模式,使只有確實被使用的資源被保留。 以上就是自定義資源保持規則相關的配置,舉個例子:
<?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools="http://schemas.android.com/tools" tools:keep="@drawable/img_*,@drawable/ic_launcher,@layout/layout_used*" tools:discard="@layout/layout_unused" tools:shrinkMode="strict"/> 複製代碼
一些替代資源,例如多語言支持的 strings.xml
,多分辨率支持的 layout.xml
等,在咱們不須要使用又不想刪除掉時,可使用資源壓縮將它們移除。 咱們使用 resConfig
屬性來指定須要支持的屬性,例如
android { defaultConfig { ... resConfigs "en", "zh" } } 複製代碼
其餘未顯式聲明的語言資源將被移除。
在項目的可執行工程Module中打開build.gradle
文件進行編輯:
android { ...... defaultConfig { ...... } buildTypes { release { minifyEnabled true // 開啓代碼混淆 zipAlignEnabled true // 開啓Zip壓縮優化 shrinkResources true // 移除未被使用的資源 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } ...... } 複製代碼
# --------------------------------------------基本指令區--------------------------------------------#
-ignorewarning # 是否忽略警告
-optimizationpasses 5 # 指定代碼的壓縮級別(在0~7之間,默認爲5)
-dontusemixedcaseclassnames # 是否使用大小寫混合(windows大小寫不敏感,建議加入)
-dontskipnonpubliclibraryclasses # 是否混淆非公共的庫的類
-dontskipnonpubliclibraryclassmembers # 是否混淆非公共的庫的類的成員
-dontpreverify # 混淆時是否作預校驗(Android不須要預校驗,去掉能夠加快混淆速度)
-verbose # 混淆時是否記錄日誌(混淆後會生成映射文件)
#指定外部模糊字典
-obfuscationdictionary dictionary1.txt
#指定class模糊字典
-classobfuscationdictionary dictionary1.txt
#指定package模糊字典
-packageobfuscationdictionary dictionary2.txt
# 混淆時所採用的算法(谷歌推薦算法)
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*,!code/allocation/variable
# 添加支持的jar(引入libs下的全部jar包)
-libraryjars libs(*.jar;)
# 將文件來源重命名爲「SourceFile」字符串
-renamesourcefileattribute SourceFile
# 保持註解不被混淆
-keepattributes *Annotation*
-keep class * extends java.lang.annotation.Annotation {*;}
# 保持泛型不被混淆
-keepattributes Signature
# 保持反射不被混淆
-keepattributes EnclosingMethod
# 保持異常不被混淆
-keepattributes Exceptions
# 保持內部類不被混淆
-keepattributes Exceptions,InnerClasses
# 拋出異常時保留代碼行號
-keepattributes SourceFile,LineNumberTable
# --------------------------------------------默認保留區--------------------------------------------#
# 保持基本組件不被混淆
-keep public class * extends android.app.Fragment
-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
# 保持 Google 原生服務須要的類不被混淆
-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService
# Support包規則
-dontwarn android.support.**
-keep public class * extends android.support.v4.**
-keep public class * extends android.support.v7.**
-keep public class * extends android.support.annotation.**
# 保持 native 方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
}
# 保留自定義控件(繼承自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);
}
# 保留在Activity中的方法參數是view的方法(避免佈局文件裏面onClick被影響)
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
# 保持枚舉 enum 類不被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# 保持R(資源)下的全部類及其方法不能被混淆
-keep class **.R$* { *; }
# 保持 Parcelable 序列化的類不被混淆(注:aidl文件不能去混淆)
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
# 須要序列化和反序列化的類不能被混淆(注:Java反射用到的類也不能被混淆)
-keepnames class * implements java.io.Serializable
# 保持 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();
}
# 保持 BaseAdapter 類不被混淆
-keep public class * extends android.widget.BaseAdapter { *; }
# 保持 CusorAdapter 類不被混淆
-keep public class * extends android.widget.CusorAdapter{ *; }
# --------------------------------------------webView區--------------------------------------------#
# WebView處理,項目中沒有使用到webView忽略便可
# 保持Android與JavaScript進行交互的類不被混淆
-keep class **.AndroidJavaScript { *; }
-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.WebChromeClient {
public void *(android.webkit.WebView,java.lang.String);
}
# 網絡請求相關
-keep public class android.net.http.SslError
# --------------------------------------------刪除代碼區--------------------------------------------#
# 刪除代碼中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(...);
}
# --------------------------------------------可定製化區--------------------------------------------#
#---------------------------------1.實體類---------------------------------
#--------------------------------------------------------------------------
#---------------------------------2.與JS交互的類-----------------------------
#--------------------------------------------------------------------------
#---------------------------------3.反射相關的類和方法-----------------------
#--------------------------------------------------------------------------
#---------------------------------2.第三方依賴--------------------------------
#--------------------------------------------------------------------------
複製代碼