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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • “风送花香红满地,雨滋春树碧连天。”是的,世界很美,不仅是因为有春的烟波画船,有夏的朝云暮卷,有秋的云霞绚烂,有冬...
    十四桥阅读 664评论 0 5
  • 【日精进打卡第18天】 515期反省二组(宁波) 【知~学习】 1:诵读六项精进大纲共164遍; 2: 诵读六项精...
    雨泽之字阅读 168评论 0 0
  • 雨来了, 别忧郁, 人生四季漫漫旅途, 那有不遇风雨。 雨来了, 别忧郁, 疾风骤雨虽然吹落花枝, 也必将催生新的...
    小蜜蜂耶阅读 77评论 2 2