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

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

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

保持指定类的所有字段

-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文件夹下输出下列文件:

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

必须保持的代码

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

混淆心得

 我们在开发过程中,可以先记录下必须保持的类及方法,后面在做混淆时,维度可以不用做的那么细致,当然如果有安全、规范要求,那就还是一步一步的走吧。只要细心一点,混淆并不复杂。

参考文章

混淆压缩官方指导文档
Android代码压缩工具R8详解
Android使用R8压缩,混淆,优化App

你可能感兴趣的:(Android R8代码混淆)