Loading...
墨滴

掀乱书页的风

2021/06/01  阅读:328  主题:默认主题

Android Gradle升级的血泪史(二)

Proguard和R8

目前Android Gradle构建插件提供了两种代码混淆工具,分别是Proguard和R8。Proguard为开源混淆工具,Java、Android都在使用,使用范围广。R8是Google于2019年专为Android构建推出的混淆工具,具有构建速度快,构建产物小的特点。Google后续会彻底移除Proguard的支持,仅支持R8混淆。

为什么要开启R8

开始升级的时候调研过程中关闭了R8,使用Proguard混淆,就是为了避免开启R8出现的一系列问题,然而人算不如天算,提测后QA发现了一个阻塞性bug,混淆打包后apk中文件丢失,导致部分RN页面加载不出来

使用Proguard文件缺失
使用Proguard文件缺失

经过各种排查尝试,发现是目前Android Gradle插件已经不再维护Proguard混淆,开启R8编译后,问题解决。

Proguard简介

Android默认集成了ProGuard,它是一个免费的用于压缩、优化和混淆Java字节码的工具,混淆的功能主要是用简短的无意义的字母组合来对代码中的类、字段、方法和属性进行重命名,但它无法对字符串进行混淆

proguard工作流程
proguard工作流程

Proguard的特性

Progurad主要有4个功能特性:默认是打开的

  1. 压缩:Proguard能通过分析字节码,能够检测并移除没有使用到的类、字段、方法和属性,减小apk的包大小。
    -dontshrink 关闭压缩,不会生成usage.txt文件
  1. 优化:优化java字节码,同时移除没有使用到的指令,应用运行更快。
    -dontoptimize  关闭优化
    -optimizationpasses n 表示proguard对代码进行迭代优化的次数,Android一般为5
  1. 混淆:使用无意义的简短字母组合对类名、字段名和方法名进行重命名,提高反编译后代码的阅读成本。
    -dontobfuscate 关闭混淆
  1. 预检验:在java平台上对处理后的代码进行预检。
    Android不需要preverify,去掉这一步可以加快混淆速度
    -dontpreverify

Proguard的生成文件

构建时Proguard默认都会输出下列文件

  1. dump.txt:列出生成的APK文件中所有class文件的内部结构。
  2. mapping.txt:列出混淆前的java源码和混淆后的类、方法和属性名字之间的映射。
  3. seeds.txt:列出未混淆的类和成员
  4. usage.txt:列出从apk文件中剥离的代码 这些文件保存在/build/outputs/mapping/release目录下

R8

官方描述

The R8 repo contains two tools:

  • D8 is a dexer that converts java byte code to dex code.
  • R8 is a java program shrinking and minification tool that converts java byte code to optimized dex code.

D8 is a replacement for the DX dexer and R8 is a replacement for the Proguard shrinking and minification tool.

个人理解

  1. 混淆角度:R8是Google专为Android构建推出的替代ProGuard的工具,具有构建速度快,构建产物小的特点。Google后续会彻底移除Proguard的支持(AGP>4.2),仅支持R8混淆
  2. 编译流程角度:R8编译流程包含2个工具R8混淆和D8编译器

R8编译对编译速度的影响

R8未引入之前的编译流程
R8未引入之前的编译流程
  1. .java和.kt代码文件被Java、Kotlin编译器协作编译为.class,而后编译为.dex文件,最后打包到.apk文件中。
  2. 从.class文件转换为dex文件的过程中,经历了ProGuard和D8(包含脱糖功能)两步
R8引入之后的编译流程
R8引入之后的编译流程

R8 将脱糖、压缩、混淆、优化和 dex 处理整合到了一个步骤中,从而显著提升了构建性能。

R8开启前后编译速度对比
R8开启前后编译速度对比
  • D8编译器:Google 在 AGP3.1 版本中引入D8编译器作为默认的DEX字节码文件编译器
  • 脱糖:当使用当前Android版本不支持的高版本jdk语法时,在编译期转换为其支持的低版本jdk语法。为什么要进行脱糖,Java 8中新增的Lambda表达式,是通过invokedynamic字节码调用指令实现的,Android的dex编译器不支持invokedynamic指令,导致Android不能直接支持Java 8,为了让开发者能用上java8的特性,所以需要在编译期间进行脱糖处理

开启R8之前,30分钟左右,开启之后15分钟左右。

R8混淆对包大小的影响

Android gradle插件与R8编译器协同工作,处理以下任务:

代码缩减(即摇树优化):从应用及其库依赖项中检测并安全地移除不使用的类、字段、方法和属性

资源缩减:从封装应用中移除不使用的资源,包括应用库依赖项中不使用的资源。此功能可与代码缩减功能结合使用,这样一来,移除不使用的代码后,也可以安全地移除不再引用的所有资源。它并不是R8引入的,而是在R8之前就存在了

混淆:缩短类和成员的名称,从而减小 DEX 文件的大小。

优化:检查并重写代码,以进一步减小应用的 DEX 文件的大小。

R8由于是专为Android构建而生,所以去掉了Proguard中的预检功能,同时作为替换Proguard的混淆工具,支持所有现有的ProGuard规则文件

包大小减小3.6M(混淆+优化)

R8混淆对开发者的影响

R8混淆是google自己的一套混淆机制,虽然兼容ProGuard的所有规则,但是开启后仍然存在一定的风险,什么风险呢,其实就是各种类,方法、变量找不到的混淆问题。那么如何在开启R8的情况下将代码改动降到最低呢?

  1. 关闭代码缩减,可以避免绝大部分的异常情况
  2. 关掉资源缩减(不得不关,后面会说)

其实这两个配置58app在使用ProGuard混淆时也是关闭的,但是在实际开启R8的过程中,仍然出现了很多混淆问题。这就是R8混淆和ProGuard混淆不一样的地方导致的

R8会强制开启代码优化
-dontoptimize是无效的,R8会忽略修改默认优化行为的所有ProGuard规则,开启优化带来的另外一个好处就是优化无用的指令,让app运行更快

资源缩减与AAB冲突

shrinkResources true开启之后,打包的时候会报错

Resource shrinker cannot be used for multi-apk applications
Unable to use shrinkResources in base module with dynamic features

Google在AGP4.2的时候才加入对AAB资源缩减的支持 image

总结

开启R8带来的收益

  1. 编译速度:R8相比ProGuard减少了15分钟,是ProGuard的2倍
  2. 包大小:减少3.6M
  3. 开启了代码优化,去除无用的指令,提升了app的运行速度

进一步优化空间

  1. 开启R8的代码缩减功能
  2. 开启R8的完全模式
    android.enableR8.fullMode=true
    R8 包含一组额外的优化功能,这些功能在默认情况下处于停用状态,这些额外的优化功能会使R8的行为与ProGuard不同,因此需要添加额外的ProGuard 规则,以避免运行时问题

也就是说代码缩减的力度会更大,也更容易出现异常问题。

掀乱书页的风

2021/06/01  阅读:328  主题:默认主题

作者介绍

掀乱书页的风