Android使用R8压缩,混淆,优化App

在Android开发中,为了使App尽可能小,可以使用R8来压缩,混淆,优化App,当使用Android Gradle插件3.4.0或更高版本时,插件不再使用ProGuard执行优化而是R8。

R8的功能

  • 代码压缩:安全地从App及其库依赖项中删除未使用的类,字段,方法和属性。
  • 资源压缩:从打包的App中删除未使用的资源,包括应用程序库依赖项中未使用的资源。它与代码压缩一起使用,这样一旦删除了未使用的代码,也可以安全地删除不再引用的资源。
  • 代码混淆:使用简短无意义的名称重命名代码里的类,字段和方法,从而减少DEX文件大小。
  • 代码优化:删除未使用的代码或重写代码使其更简洁。

R8 和 Proguard

R8和Proguard 相比,R8 可以更快地缩减代码,同时改善输出大小,R8 默认处于启用状态,你可将以下代码添加到项目的 gradle.properties 文件以停用 R8:

android.enableR8=false

R8 普通模式是兼容 Proguard的,R8 完全模式与会启用一些额外的优化,这个时候可能需要一些其它ProGuard规则以避免运行时问题,可以在 项目的gradle.properties 文件中设置以下内容启用完全模式。

android.enableR8.fullMode=true

启用压缩,混淆,优化

使用Android Studio创建新项目时,默认情况下不启用压缩,混淆,优化,因为这会增加项目的构建时间,而且有些代码混淆后会出错。要想启用这些功能,需要在项目的build.gradle包含下面的内容。

getDefaultProguardFile('proguard-android.txt') 方法可从 Android SDK tools/proguard/ 文件夹获取默认的 ProGuard规则文件。

proguard-rules.pro文件用于添加自定义 ProGuard 规则。默认情况下,该文件位于模块根目录。

android {
    buildTypes {
        release {
            //启用代码压缩,混淆,优化
            minifyEnabled true

            //启用资源压缩
            shrinkResources true

            //ProGuard规则文件
            proguardFiles getDefaultProguardFile(
                    'proguard-android-optimize.txt'),
                    'proguard-rules.pro'
        }
    }
    ...
}

自定义要保留的资源

如果您有想要保留或舍弃的特定资源,请在您的项目中创建一个包含 标记的 XML 文件,并在 tools:keep 属性中指定每个要保留的资源,在 tools:discard 属性中指定每个要舍弃的资源。这两个属性都接受逗号分隔的资源名称列表。您可以使用星号字符作为通配符。



严格压缩模式

如果你的代码或库代码(例如AppCompat)调用了Resources.getIdentifier(),这就表示你的代码将根据动态生成的字符串查询资源名称,默认情况下资源压缩器会采取防御性行为,将所有具有匹配名称格式的资源标记为可能已使用,无法移除。例如,以下代码会使所有带 img_ 前缀的资源标记为已使用。

String name = String.format("img_%1d", angle + 1);
res = getResources().getIdentifier(name, "drawable", getPackageName());

在 keep.xml 文件中将 shrinkMode 设置为 strict可以停用该防御性行为,这个时候你必须用tools:keep 属性手动保留这些资源。



合并重复资源

默认情况下,Gradle 还会合并同名资源,例如可能位于不同资源文件夹中的同名可绘制对象。这一行为不受 shrinkResources 属性控制,也无法停用,因为在有多个资源匹配代码查询的名称时,有必要利用这一行为来避免错误。

只有在两个或更多个文件具有完全相同的资源名称类型限定符时,才会进行资源合并。

自定义要保持的代码

在ProGuard规则文件中可以使用-keep保持特定代码不被移除或混淆,或者向你想保持的代码添加 @Keep 注解,在类上添加 @Keep 可原样保持整个类,在方法或字段上添加它可完整保持方法/字段(及其名称)以及类名称。

-keep public class MyClass

必须保持的代码

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

常用ProGuard规则

关闭压缩

-dontshrink

关闭代码优化,默认Proguard规则文件已包含

-dontoptimize

关闭混淆

-dontobfuscate

指定代码优化级别,值在0-7之间,默认为5

-optimizationpasses 5

混淆时不使用大小写混合类名,默认Proguard规则文件已包含

-dontusemixedcaseclassnames

不忽略库中的非public的类,默认Proguard规则文件已包含

-dontskipnonpubliclibraryclasses

不忽略库中的非public的类成员

-dontskipnonpubliclibraryclassmembers

输出详细信息,默认Proguard规则文件已包含

-verbose

不做预校验,预校验是作用在Java平台上的,Android平台上不需要这项功能,去掉之后还可以加快混淆速度,默认Proguard规则文件已包含

-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.**

特殊ProGuard规则

由于enum类的特殊性,下面两个方法会被反射调用,默认Proguard规则文件已经处理。

-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

Parcelable的子类和Creator静态成员变量要保持,否则会产生Android.os.BadParcelableException异常,默认Proguard规则文件已经处理。

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

常用混淆模板

# 指定代码的压缩级别
-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或ProGuard构建项目后会在模块下的build\outputs\mapping\release文件夹下输出下列文件:

  • dump.txt:说明 APK 中所有类文件的内部结构。
  • mapping.txt:提供原始与混淆过的类、方法和字段名称之间的转换。
  • seeds.txt:列出未进行混淆的类和成员。
  • usage.txt:列出从 APK 移除的代码。

你可能感兴趣的:(Android使用R8压缩,混淆,优化App)