?Android Gradle插件升級(jí)至3.4.0版本之后,帶來(lái)一個(gè)新特性-新一代混淆工具R8阁猜,做為D8的升級(jí)版替代Proguard颗味;在應(yīng)用壓縮、應(yīng)用優(yōu)化方面提供更極致的體驗(yàn)些膨。
R8 和 Proguard
?R8 一步到位地完成了所有的縮減(shrinking),去糖(desugaring)和 轉(zhuǎn)換成 Dalvik 字節(jié)碼(dexing )過(guò)程钦铺。
縮減(shrinking)過(guò)程實(shí)現(xiàn)以下三個(gè)重要的功能:
- 代碼縮減:從應(yīng)用及其庫(kù)依賴項(xiàng)中檢測(cè)并安全地移除未使用的類(lèi)订雾、字段、方法和屬性矛洞。
- 資源縮減:從封裝應(yīng)用中移除不使用的資源洼哎,包括應(yīng)用庫(kù)依賴項(xiàng)中的不使用的資源。
- 優(yōu)化:檢查并重寫(xiě)代碼,以進(jìn)一步減小應(yīng)用的 DEX 文件的大小噩峦。
- 混淆:縮短類(lèi)和成員的名稱锭沟,從而減小 DEX 文件的大小。
?R8 和當(dāng)前的代碼縮減解決方案 Proguard 相比识补,R8 可以更快地縮減代碼族淮,同時(shí)改善輸出大小。下面將通過(guò)幾張數(shù)據(jù)圖來(lái)對(duì)比(數(shù)據(jù)源自于 benchmark):
R8混淆使用
?R8的使用非常簡(jiǎn)單凭涂,使用方式與Proguard并無(wú)差異祝辣,Android Studio創(chuàng)建項(xiàng)目時(shí)默認(rèn)是關(guān)閉的,因?yàn)檫@會(huì)加長(zhǎng)工程打包時(shí)間切油,所以開(kāi)發(fā)階段不建議開(kāi)啟较幌。開(kāi)啟方式如下:
buildTypes {
release {
// 啟用代碼收縮、混淆和優(yōu)化白翻。
minifyEnabled true
// 啟用資源縮減
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
?可以看到gradle中加載了兩個(gè)混淆配置文件, 其中 proguard-rules.pro 供開(kāi)發(fā)者自定義混淆規(guī)則绢片;proguard-android-optimize.txt 這是默認(rèn)的配置文件滤馍,包含一些通用的混淆規(guī)則,在sdk/tools/proguard目錄下底循,其中包含的內(nèi)容如下:
# 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常用規(guī)則
關(guān)閉壓縮
-dontshrink
關(guān)閉代碼優(yōu)化
-dontoptimize
關(guān)閉混淆
-dontobfuscate
指定代碼優(yōu)化級(jí)別巢株,值在0-7之間,默認(rèn)為5
-optimizationpasses 5
混淆時(shí)不使用大小寫(xiě)混合類(lèi)名
-dontusemixedcaseclassnames
不忽略庫(kù)中的非public的類(lèi)
-dontskipnonpubliclibraryclasses
不忽略庫(kù)中的非public的類(lèi)成員
-dontskipnonpubliclibraryclassmembers
輸出詳細(xì)信息
-verbose
不做預(yù)校驗(yàn)熙涤,預(yù)校驗(yàn)是作用在Java平臺(tái)上的阁苞,Android平臺(tái)上不需要這項(xiàng)功能,去掉之后還可以加快混淆速度
-dontpreverify
保持指定包下的類(lèi)名祠挫,不包括子包下的類(lèi)名
-keep class com.xy.myapp*
保持指定包下的類(lèi)名那槽,包括子包下的類(lèi)名
-keep class com.xy.myapp**
保持指定包下的類(lèi)名以及類(lèi)里面的內(nèi)容
-keep class com.xy.myapp.* {*;}
保持所有繼承于指定類(lèi)的類(lèi)
-keep public class * extends android.app.Activity
其它keep方法:
保留 | 防止被移除或者被混淆 | 防止被混淆 |
---|---|---|
類(lèi)和類(lèi)成員 | -keep | -keepnames |
僅類(lèi)成員 | -keepclassmembers | -keepclassmembernames |
如果擁有某成員,保留類(lèi)和類(lèi)成員 | -keepclasseswithmembers | -keepclasseswithmembernames |
如果我們要保留一個(gè)類(lèi)中的內(nèi)部類(lèi)不被混淆則需要用$符號(hào)等舔,如下例子表示保持MyClass內(nèi)部類(lèi)JavaScriptInterface中的所有public內(nèi)容骚灸。
-keepclassmembers class com.xy.myapp.MyClass$JavaScriptInterface {
public *;
}
保持指定類(lèi)的所有方法
-keep class com.xy.myapp.MyClass {
public <methods>;
}
保持指定類(lèi)的所有字段
-keep class com.xy.myapp.MyClass {
public <fields>;
}
保持指定類(lèi)的所有構(gòu)造器
-keep class com.xy.myapp.MyClass {
public <init>;
}
保持用指定參數(shù)作為形參的方法
-keep class com.xy.myapp.MyClass {
public <methods>(java.lang.String);
}
類(lèi)文件除了定義類(lèi),字段慌植,方法外甚牲,還為它們附加了一些屬性,例如注解蝶柿,異常丈钙,行號(hào)等,優(yōu)化操作會(huì)刪除不必要的屬性交汤,使用-keepattributes可以保留指定的屬性
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,
SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
使指定的類(lèi)不輸出警告信息
-dontwarn com.squareup.okhttp.**
常用混淆模版
# 指定代碼的壓縮級(jí)別
-optimizationpasses 5
# 不忽略庫(kù)中的非public的類(lèi)成員
-dontskipnonpubliclibraryclassmembers
# google推薦算法
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
# 避免混淆Annotation雏赦、內(nèi)部類(lèi)、泛型、匿名類(lèi)
-keepattributes *Annotation*,InnerClasses,Signature,EnclosingMethod
# 拋出異常時(shí)保留代碼行號(hào)
-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下的所有類(lèi)及其內(nèi)部類(lèi)
-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);
}
# 保持所有實(shí)現(xiàn) Serializable 接口的類(lèi)成員
-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構(gòu)建項(xiàng)目后會(huì)在模塊下的build\outputs\mapping\release文件夾下輸出下列文件:
- dump.txt:說(shuō)明 APK 中所有類(lèi)文件的內(nèi)部結(jié)構(gòu)喉誊。
- mapping.txt:提供原始與混淆過(guò)的類(lèi)邀摆、方法和字段名稱之間的轉(zhuǎn)換。
- seeds.txt:列出未進(jìn)行混淆的類(lèi)和成員伍茄。
- usage.txt:列出從 APK 移除的代碼栋盹。
必須保持的代碼
- AndroidManifest.xml引用的類(lèi)。
- JNI調(diào)用的方法敷矫。
- 反射用到的類(lèi)例获。
- WebView中JavaScript使用的類(lèi)。
- Layout文件引用的自定義View曹仗。
混淆心得
?我們?cè)陂_(kāi)發(fā)過(guò)程中榨汤,可以先記錄下必須保持的類(lèi)及方法,后面在做混淆時(shí)怎茫,維度可以不用做的那么細(xì)致收壕,當(dāng)然如果有安全、規(guī)范要求轨蛤,那就還是一步一步的走吧蜜宪。只要細(xì)心一點(diǎn),混淆并不復(fù)雜祥山。
參考文章
混淆壓縮官方指導(dǎo)文檔
Android代碼壓縮工具R8詳解
Android使用R8壓縮圃验,混淆,優(yōu)化App