Android Gradle插件升级至3.4.0版本之后,带来一个新特性-新一代混淆工具R8,做为D8的升级版替代Proguard;在应用压缩、应用优化方面提供更极致的体验。
Gradle插件版本3.4.0之后:
R8 默认处于启用状态:R8 将脱糖、压缩、混淆、优化和 dex 处理整合到了一个步骤中,从而显著提升了构建性能。 R8 是在 Android Gradle 插件 3.3.0 中引入的,对于使用插件 3.4.0 及更高版本的应用和 Android 库项目,R8 现已默认处于启用状态。
下图是 R8 引入之前的编译流程的简要概览。
现在,有了 R8,可以在一个步骤中完成脱糖、压缩、混淆、优化和 dex 处理 (D8),如下图所示。
请注意,R8 旨在与您现有的 ProGuard 规则配合使用,因此您可能不需要采取任何操作即可从 R8 中受益。但是,相对专为 Android 项目设计的 ProGuard 而言,R8 是一项不同的技术,因此压缩和优化可能会导致移除 ProGuard 可能没有的代码。因此,在这种情况(尽管不太可能发生)下,您可能需要添加其他规则,以在构建输出中保留这些代码。
如果您在使用 R8 时遇到问题,请阅读 R8 兼容性常见问题解答一文,以检查是否有针对您的问题的解决方案。如果没有记录的解决方案,请报告错误。您可以停用 R8,只需将以下其中一行代码添加到项目的 gradle.properties
文件即可:
# Disables R8 for Android Library modules only.
android.enableR8.libraries = false
# Disables R8 for all modules.
android.enableR8 = false
注意:对于指定构建类型,如果您在应用模块的 build.gradle
文件中将 useProguard
设为 false
,Android Gradle 插件会使用 R8 压缩该构建类型的应用代码,无论您是否在项目的 gradle.properties
文件中停用 R8 都是如此。
R8 一步到位地完成了所有的缩减(shrinking),去糖(desugaring)和 转换成 Dalvik 字节码(dexing )过程。
缩减(shrinking)过程实现以下三个重要的功能:
- 代码缩减:从应用及其库依赖项中检测并安全地移除未使用的类、字段、方法和属性。
- 资源缩减:从封装应用中移除不使用的资源,包括应用库依赖项中的不使用的资源。
- 优化:检查并重写代码,以进一步减小应用的 DEX 文件的大小。
- 混淆:缩短类和成员的名称,从而减小 DEX 文件的大小。
R8 和当前的代码缩减解决方案 Proguard 相比,R8 可以更快地缩减代码,同时改善输出大小。下面将通过几张数据图来对比(数据源自于 benchmark):
shrinkingTime.png
dexSize.png
apkSize.png
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 ;
}
# 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 ;
}
# 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 ;
}
-keepclasseswithmembers class * {
@android.support.annotation.Keep ;
}
-keepclasseswithmembers class * {
@android.support.annotation.Keep (...);
}
关闭压缩
-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 ;
}
保持指定类的所有字段
-keep class com.xy.myapp.MyClass {
public ;
}
保持指定类的所有构造器
-keep class com.xy.myapp.MyClass {
public ;
}
保持用指定参数作为形参的方法
-keep class com.xy.myapp.MyClass {
public (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 (android.content.Context);
public (android.content.Context, android.util.AttributeSet);
public (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文件夹下输出下列文件:
- AndroidManifest.xml引用的类。
- JNI调用的方法。
- 反射用到的类。
- WebView中JavaScript使用的类。
- Layout文件引用的自定义View。
我们在开发过程中,可以先记录下必须保持的类及方法,后面在做混淆时,维度可以不用做的那么细致,当然如果有安全、规范要求,那就还是一步一步的走吧。只要细心一点,混淆并不复杂。
混淆压缩官方指导文档
Android代码压缩工具R8详解
Android使用R8压缩,混淆,优化App
Android代码压缩工具R8详解 android.enableR8=true
作者:望着天数月亮
链接:https://www.jianshu.com/p/fdadca8e2094
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。