為什麼Java 需要obfuscate呢?

這跟他的出生有關,

因為在執行的時候, 都需要通過 JVM(Java Virtual Machine)來作轉換讓該機器可以辨別並做事情

為此JVM也需要一種解析格式來轉換, 而其格式就是java 編譯過的class檔

藉此若有心人反組譯回去的話, 便可以知道所有的程式碼, 並且串改

所以混淆(Obfuscator)是java工程師必須做的保護程式碼的事情

這裡就不講怎麼反組譯了, 而是談Android程式碼混淆的部分,

也稱為Proguard 

 

Android 本身也是由java 開發(雖然有kotlin開發)

不過要改變整個Android生態, 個人覺得google 還需要很大的決心

如向上相容問題, 必須好好的規劃, 不然舊手機以後就不能用了

Proguard 也有個好處, 可以讓apk瘦身, 因為縮減程式碼嘛~!

 

設定上很簡單

只需要在build.gradle 中作設定就好了

    buildTypes {
        release {
            minifyEnabled true // To enable code shrinking with ProGuard
            shrinkResources true // 移除未使用的code, 簡化apk 
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

        }
    }

 

shrinkResources 相關資訊可參考 Shrink Your Resources

接著設定 proguard 腳本

在本範例中也是proguard-rules.pro這個檔案

若沒有這檔案可以在app name 資料夾中新增檔案

其檔案內容如下

# Add project specific ProGuard rules here.
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# 代码混淆壓缩比,在0~7之间,默認為5,一般不做修改
-optimizationpasses 5

# 混合时不使用大小寫混合,混合后的類名为小寫
-dontusemixedcaseclassnames

# 指定不去忽略非公共庫的類
-dontskipnonpubliclibraryclasses

# 這句話能夠使我們的項目混淆後產生映射文件
# 包含有類名->混淆後類名的映射關係
-verbose

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

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

# 保留Annotation不混淆
-keepattributes *Annotation*,InnerClasses

# 避免混淆簽章
-keepattributes Signature

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

# 指定混淆是採用的算法,後面的參數是一個過濾器
# 這個過濾器是谷歌推薦的算法,一般不做更改
-optimizations !code/simplification/cast,!field/*,!class/merging/*

#############################################
#
# Android開發中一些需要保留的公共部分
#
#############################################
# 保留我們使用的四大組件,自定義的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.android.vending.licensing.ILicensingService

# 保留support下的所有類及其內部類
-keep class android.support.** {*;}

# 保留继承的
-keep public class * extends android.support.v4.**
-keep public class * extends android.support.v7.**
-keep public class * extends android.support.annotation.**

# 保留R下面的资源
-keep class **.R$* {*;}

# 保留在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)不被混淆
-keep public class * extends android.view.View{
    *** get*();
    void set*(***);
    public (android.content.Context);
    public (android.content.Context, android.util.AttributeSet);
    public (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 ;
    !private ;
    !private ;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

-keepclassmembers class * {
    void *(**On*Event);
    void *(**On*Listener);
}

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

#------------處理第三方套件--------------
# Facebook
-keep class com.facebook.** {*;}
-keep interface com.facebook.** {*;}
-keep enum com.facebook.** {*;}

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

# Gson
-dontwarn com.google.gson.**
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.stream.** { *; }

# OkHttp
-keep class com.squareup.okhttp.** { *; }
-keep interface com.squareup.okhttp.** { *; }
-dontwarn com.squareup.okhttp.**

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

#android-async-http
-dontwarn com.loopj.android.http.**
-keep class com.loopj.android.http.**{*;}

#brightcove
-dontwarn com.brightcove.**
-keep class com.brightcove.** {*;}

#comscore
-keep class com.comscore.** { *; }
-dontwarn com.comscore.**

#ad
-keep public class com.google.android.gms.ads.** {
   public *;
}
-keep public class com.google.ads.** {
   public *;
}

#Crashlytics
-keep class com.crashlytics.** { *; }
-dontwarn com.crashlytics.**

# Required to preserve the Flurry SDK
-keep class com.flurry.** { *; }
-dontwarn com.flurry.**
-keepattributes *Annotation*,EnclosingMethod,Signature
-keepclasseswithmembers class * {
    public (android.content.Context, android.util.AttributeSet, int);
}

 

注意事項

用來json格式時, 使用class 解析並轉換的方式, 或許會造成資料為空值得情況

範例如下:

1. Java 反射機制 invoke, method name等等;

2. Gson.fromJson(json.toString(), SomeBody.class);

因為proguard 預設會把該class name 與 params name給重新命名

所以反射後會找不到該名字 (String content 不會更動) 

若有使用gson套件轉化的

建議把他們集中在同一個package中

再keep 那個package 就可以避免囉

這部分大家需要好好注意

 

註一: 

arrow
arrow

    Owen Chen 發表在 痞客邦 留言(1) 人氣()