Android R8代碼混淆

?Android Gradle插件升級至3.4.0版本之后,帶來一個新特性-新一代混淆工具R8,做為D8的升級版替代Proguard;在應用壓縮、應用優化方面提供更極致的體驗。

R8 和 Proguard

?R8 一步到位地完成了所有的縮減(shrinking),去糖(desugaring)和 轉換成 Dalvik 字節碼(dexing )過程。

縮減(shrinking)過程實現以下三個重要的功能:

  • 代碼縮減:從應用及其庫依賴項中檢測并安全地移除未使用的類、字段、方法和屬性。
  • 資源縮減:從封裝應用中移除不使用的資源,包括應用庫依賴項中的不使用的資源。
  • 優化:檢查并重寫代碼,以進一步減小應用的 DEX 文件的大小。
  • 混淆:縮短類和成員的名稱,從而減小 DEX 文件的大小。

?R8 和當前的代碼縮減解決方案 Proguard 相比,R8 可以更快地縮減代碼,同時改善輸出大小。下面將通過幾張數據圖來對比(數據源自于 benchmark):


shrinkingTime.png
dexSize.png

apkSize.png

R8混淆使用

?R8的使用非常簡單,使用方式與Proguard并無差異,Android Studio創建項目時默認是關閉的,因為這會加長工程打包時間,所以開發階段不建議開啟。開啟方式如下:

buildTypes {
        release {
            // 啟用代碼收縮、混淆和優化。
            minifyEnabled true
            // 啟用資源縮減
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

?可以看到gradle中加載了兩個混淆配置文件, 其中 proguard-rules.pro 供開發者自定義混淆規則;proguard-android-optimize.txt 這是默認的配置文件,包含一些通用的混淆規則,在sdk/tools/proguard目錄下,其中包含的內容如下:

# This is a configuration file for ProGuard.
# http://proguard.sourceforge.net/index.html#manual/usage.html
#
# This file is no longer maintained and is not used by new (2.2+) versions of the
# Android plugin for Gradle. Instead, the Android plugin for Gradle generates the
# default rules at build time and stores them in the build directory.

# Optimizations: If you don't want to optimize, use the
# proguard-android.txt configuration file instead of this one, which
# turns off the optimization flags.  Adding optimization introduces
# certain risks, since for example not all optimizations performed by
# ProGuard works on all versions of Dalvik.  The following flags turn
# off various optimizations known to have issues, but the list may not
# be complete or up to date. (The "arithmetic" optimization can be
# used if you are only targeting Android 2.0 or later.)  Make sure you
# test thoroughly if you go this route.
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
-optimizationpasses 5
-allowaccessmodification
-dontpreverify

# The remainder of this file is identical to the non-optimized version
# of the Proguard configuration file (except that the other file has
# flags to turn off optimization).

-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose

-keepattributes *Annotation*
-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService

# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
-keepclasseswithmembernames class * {
    native <methods>;
}

# keep setters in Views so that animations can still work.
# see http://proguard.sourceforge.net/manual/examples.html#beans
-keepclassmembers public class * extends android.view.View {
   void set*(***);
   *** get*();
}

# We want to keep methods in Activity that could be used in the XML attribute onClick
-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}

# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

-keepclassmembers class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator CREATOR;
}

-keepclassmembers class **.R$* {
    public static <fields>;
}

# The support library contains references to newer platform versions.
# Don't warn about those in case this app is linking against an older
# platform version.  We know about them, and they are safe.
-dontwarn android.support.**

# Understand the @Keep support annotation.
-keep class android.support.annotation.Keep

-keep @android.support.annotation.Keep class * {*;}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <methods>;
}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <fields>;
}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <init>(...);
}

ProGuard常用規則

關閉壓縮

-dontshrink

關閉代碼優化

-dontoptimize

關閉混淆

-dontobfuscate

指定代碼優化級別,值在0-7之間,默認為5

-optimizationpasses 5

混淆時不使用大小寫混合類名

-dontusemixedcaseclassnames

不忽略庫中的非public的類

-dontskipnonpubliclibraryclasses

不忽略庫中的非public的類成員

-dontskipnonpubliclibraryclassmembers

輸出詳細信息

-verbose

不做預校驗,預校驗是作用在Java平臺上的,Android平臺上不需要這項功能,去掉之后還可以加快混淆速度

-dontpreverify

保持指定包下的類名,不包括子包下的類名

-keep class com.xy.myapp*

保持指定包下的類名,包括子包下的類名

-keep class com.xy.myapp**

保持指定包下的類名以及類里面的內容

-keep class com.xy.myapp.* {*;}

保持所有繼承于指定類的類

-keep public class * extends android.app.Activity

其它keep方法:

保留 防止被移除或者被混淆 防止被混淆
類和類成員 -keep -keepnames
僅類成員 -keepclassmembers -keepclassmembernames
如果擁有某成員,保留類和類成員 -keepclasseswithmembers -keepclasseswithmembernames

如果我們要保留一個類中的內部類不被混淆則需要用$符號,如下例子表示保持MyClass內部類JavaScriptInterface中的所有public內容。

-keepclassmembers class com.xy.myapp.MyClass$JavaScriptInterface {
   public *;
}

保持指定類的所有方法

-keep class com.xy.myapp.MyClass {
    public <methods>;
}

保持指定類的所有字段

-keep class com.xy.myapp.MyClass {
    public <fields>;
}

保持指定類的所有構造器

-keep class com.xy.myapp.MyClass {
    public <init>;
}

保持用指定參數作為形參的方法

-keep class com.xy.myapp.MyClass {
    public <methods>(java.lang.String);
}

類文件除了定義類,字段,方法外,還為它們附加了一些屬性,例如注解,異常,行號等,優化操作會刪除不必要的屬性,使用-keepattributes可以保留指定的屬性

-keepattributes Exceptions,InnerClasses,Signature,Deprecated,
                SourceFile,LineNumberTable,*Annotation*,EnclosingMethod

使指定的類不輸出警告信息

-dontwarn com.squareup.okhttp.**

常用混淆模版

# 指定代碼的壓縮級別
-optimizationpasses 5     

# 不忽略庫中的非public的類成員
-dontskipnonpubliclibraryclassmembers 

# google推薦算法
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*

# 避免混淆Annotation、內部類、泛型、匿名類
-keepattributes *Annotation*,InnerClasses,Signature,EnclosingMethod

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

# 保持四大組件
-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

# 保持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.**

# 保持自定義控件
-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);
}

# 保持所有實現 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();
}


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

輸出文件

?啟用R8構建項目后會在模塊下的build\outputs\mapping\release文件夾下輸出下列文件:

  • dump.txt:說明 APK 中所有類文件的內部結構。
  • mapping.txt:提供原始與混淆過的類、方法和字段名稱之間的轉換。
  • seeds.txt:列出未進行混淆的類和成員。
  • usage.txt:列出從 APK 移除的代碼。

必須保持的代碼

  • AndroidManifest.xml引用的類。
  • JNI調用的方法。
  • 反射用到的類。
  • WebView中JavaScript使用的類。
  • Layout文件引用的自定義View。

混淆心得

?我們在開發過程中,可以先記錄下必須保持的類及方法,后面在做混淆時,維度可以不用做的那么細致,當然如果有安全、規范要求,那就還是一步一步的走吧。只要細心一點,混淆并不復雜。

參考文章

混淆壓縮官方指導文檔
Android代碼壓縮工具R8詳解
Android使用R8壓縮,混淆,優化App

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • “風送花香紅滿地,雨滋春樹碧連天。”是的,世界很美,不僅是因為有春的煙波畫船,有夏的朝云暮卷,有秋的云霞絢爛,有冬...
    十四橋閱讀 693評論 0 5
  • 【日精進打卡第18天】 515期反省二組(寧波) 【知~學習】 1:誦讀六項精進大綱共164遍; 2: 誦讀六項精...
    雨澤之字閱讀 176評論 0 0
  • 雨來了, 別憂郁, 人生四季漫漫旅途, 那有不遇風雨。 雨來了, 別憂郁, 疾風驟雨雖然吹落花枝, 也必將催生新的...
    小蜜蜂耶閱讀 97評論 2 2