Android開發中的混淆及配置

寫在前面
混淆在我們實際的Android開發中是必不可少的,如果不混淆, 發佈出去,別人一反編譯 就可以直接看你的源碼了,那麼在應用發佈前,就需要對代碼進行混淆處理,從而讓我們代碼即使被反編譯,也難以閱讀。
ProGuard作用
proguard擁有以下四個功能。
壓縮(Shrink):偵測並移除代碼中無用的類、字段、方法、和特性(Attribute)。
優化(OPtimize):對字節碼進行優化,移除無用指令。
混淆(Obfuscate):使用a、b、c、d這樣簡短而無意義的名稱,對類、字段和方法進行重命名。
預檢(Preveirfy): 在java平臺上對處理後的代碼進行預檢。
Android開發中的混淆
這裏說明下Android Studio中的使用情況,在開發工具Android Studio中集成了ProGuard混淆的功能,在Android Sdk “tools\proguard\lib\proguard.jar“目錄下,可以自行的查看、替換。它結合了gradle搭配使用。
在gradle中的使用:

buildTypes {  
    debug {  
        versionNameSuffix ".debug"  
    }  

    release {  
        debuggable false  
        minifyEnabled true  //將minifyEnabled 屬性設置爲true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'  
        signingConfig signingConfigs.release  
    }  
} 

proguard-rules.pro在項目的module結構中:


混淆文件的位置
新生成的proguard-rules.pro都是沒有添加混淆規則的,這需要我們手動的去編寫、添加。

混淆配置
在項目中的混淆配置一般可分爲兩大類:
一、基本不變的混淆(系統推薦的不可混淆的文件配置)
二、自己自行添加的配置(實體類、依賴的第三方框架、與wevview交互的js、一些反射的類)
基本不變的混淆

# 代碼混淆壓縮比,在0~7之間,默認爲5,一般不下需要修改
-optimizationpasses 5

# 混淆時不使用大小寫混合,混淆後的類名爲小寫 windows下的同學還是加入這個選項吧(windows大小寫不敏感)
-dontusemixedcaseclassnames

# 指定不去忽略非公共的庫的類
# 默認跳過,有些情況下編寫的代碼與類庫中的類在同一個包下,並且持有包中內容的引用,此時就需要加入此條聲明
-dontskipnonpubliclibraryclasses

# 指定不去忽略非公共的庫的類的成員
-dontskipnonpubliclibraryclassmembers

# 不做預檢驗,preverify是proguard的四個步驟之一 Android不需要preverify,去掉這一步可以加快混淆速度
-dontpreverify

# 混淆後就會生成映射文件 包含有類名->混淆後類名的映射關係 然後使用printmapping指定映射文件的名稱
-verbose
-printmapping priguardMapping.txt

# 保護代碼中的Annotation不被混淆 這在JSON實體映射時非常重要,比如fastJson
-keepattributes *Annotation*

# 避免混淆泛型 這在JSON實體映射時非常重要,比如fastJson
-keepattributes Signature

# 拋出異常時保留代碼行號
-keepattributes SourceFile,LineNumberTable

# 指定混淆時採用的算法,後面的參數是一個過濾器 這個過濾器是谷歌推薦的算法,一般不改變
-optimizations !code/simplification/artithmetic,!field/*,!class/merging/*
--------------------------------------------------------------------------
# 保留了繼承自Activity、Application這些類的子類 不被混淆
-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

# 保留所有的本地native方法不被混淆
-keepclasseswithmembernames class * {
    native <methods>;
}

# 保留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) 並且 get、set方法 不被混淆 
-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* ();
}
-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{
    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);
}

# 如果有引用android-support-v4.jar包,可以添加下面這行
-keep public class fragment所在文件的路徑.** {*;}
# 避免NoClassDefFoundError異常 同上一起使用
-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


# 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);
}

自己自行添加的配置(可選)
實體類不被混淆
在與服務器端交互,將json數據映射到本地的實體類,不被混淆,建議將所有的實體類放在統一路徑下,方便管理。

# com.example.entity是存放實體類的路徑
-keep class com.xx.entity.** {
    //全部忽略
    *;
}

第三方依賴
這部分要根據自己項目中依賴的框架而定,常見的配置如下:

#---------------------------------常見第三方配置(可選)-------------------------------

#eventBus
-keepattributes *Annotation*
-keepclassmembers class ** {
    @org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
    <init>(java.lang.Throwable);
}

#glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
  **[] $VALUES;
  public *;
}

#butterknife
-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }
-keepclasseswithmembernames class * {
    @butterknife.* <fields>;
}
-keepclasseswithmembernames class * {
    @butterknife.* <methods>;
}
#gson
-keepattributes Signature-keepattributes *Annotation*
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.stream.** { *; }
# Application classes that will be serialized/deserialized over Gson 下面替換成自己的實體類
-keep class com.example.bean.** { *; }

#retrofit2
-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;
}

#okhttp3
-dontwarn com.squareup.okhttp3.**
-keep class com.squareup.okhttp3.** { *;}
-dontwarn okio.**

#友盟統計
-keepclassmembers class * { 
  public <init> (org.json.JSONObject);
}
-keep public class [您的應用包名].R$*{
  public static final int *;
}
-keepclassmembers enum * {
  public static **[] values(); public static ** valueOf(java.lang.String);
}
#極光推送
-dontoptimize
-dontpreverify

-dontwarn cn.jpush.**
-keep class cn.jpush.** { *; }
-keep class * extends cn.jpush.android.helpers.JPushMessageReceiver { *; }

-dontwarn cn.jiguang.**
-keep class cn.jiguang.** { *; }

#-------------------------------------------------------------------------

注:以上配置都是常見的第三方混淆,如有不當可以自行google,以最新的官網文檔爲準。
內部類、js交互、反射
一些內部類不希望被混淆,與js交互的不被混淆。

# 指定反射類所在的包 不被混淆
-keep class 你的類所在的包.** { *; }

# 你的類所在的包.父類$子類 { <methods>; }
-keepclasseswithmembers class com.example.ui.MainActivity$JSInterface { 
      <methods>; 
}

混淆配置注意事項:
1.反射用到的類不混淆
2.JNI方法不混淆
3.AndroidMainfest中的類不混淆,四大組件和Application的子類和Framework層下所有的類默認不會進行混淆
4.Parcelable的子類和Creator靜態成員變量不混淆,否則會產生android.os.BadParcelableException異常
5.使用GSON、fastjson等框架時,所寫的JSON對象類不混淆,否則無法將JSON解析成對應的對象
6.使用第三方開源庫或者引用其他第三方的SDK包時,需要在混淆文件中加入對應的混淆規則
7.有用到WebView的JS調用也需要保證寫的接口方法不混淆

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章