向工程腐化开炮 | proguard治理

作者:刘天宇(谦风)

工程腐化是app迭代过程中,一个非常棘手的问题,涉及到广泛而细碎的具体细节,对研发效能&体验、工程&产物质量、稳定性、包大小、性能,都有相对“隐蔽”而间接的影响。一般不会造成不可承受的障碍,却时常蹦出来导致“阵痛”,有点像蛀牙或智齿,到了一定程度不拔不行,但不同的是,工程的腐化很难通过一次性“拔除”来根治,任何一次“拔除”之后,需要有效的可持续治理方案,形成常态化的防腐体系。

工程腐化拆解来看,是组成app的代码工程中,工程结构本身,以及各类“元素”(manifest、代码、资源、so、配置)的腐化。优酷架构团队近年来,持续在进行思考、实践与治理,并沉淀了一些技术、工具、方案。现逐一分类汇总,辅以相关领域知识讲解,整理成为《向工程腐化开炮》系列技术文章,分享给大家。希望更多同学,一起加入到与工程腐化的这场持久战中。

本文为系列文章首篇,将聚焦于java代码proguard,这一细分领域。对工程腐化,直接开炮!

在Android(java)开发领域,一般提到“代码proguard”,是指利用Proguard工具对java代码进行裁剪、优化、混淆处理,从而实现无用代码删除(tree-shaking)、代码逻辑优化、符号(类、变量、方法)混淆。proguard处理过程,对apk构建耗时、产物可控性(运行时稳定性)、包大小、性能,都有重要影响。

很多时候开发者会用“混淆”来代指整个Proguard处理,虽然不准确,但结合语境来理解,只要不产生歧义,也无伤大雅。值得注意的是,google官方已经在近几年的Android Gradle Plugin中,使用自研的R8工具替代了Proguard工具,来完成上述三个功能。但“代码proguard”的说法,已经形成惯用语,在本文中除非特别说明,“代码proguard”就是指处理过程,而非Proguard工具本身。

基础知识

本章先简要介绍一些基础知识,方便大家对proguard有一个“框架性”的清晰认知。

功能介绍

Proguard的三个核心功能,作用如下:

  • 裁剪(shrink)。通过对所有代码引用关系,进行整体性的静态分析,检测并移除无用的类、变量、方法、属性。对最终apk的减小,具有重要作用;
  • 优化(optimize)。这是整个Proguard处理过程中,最复杂的一部分。通过对代码执行逻辑的深层次分析,移除无用的代码分支、方法参数、本地变量,对方法/类进行内联,甚至是优化指令集合,总计包含几十项优化项。一方面可以降低代码大小占用,另一方面,也是最为重要的,是能够降低运行时方法执行耗时;
  • 混淆(obfuscate)。通过缩短类、变量、方法名称的方式,降低代码大小占用,对最终apk的减小,同样具有重要作用。同时,也是增加apk防破解难度的一个初级技术方案。

上述三个处理过程,shrink和optimize交替进行,根据配置可以循环多次(R8不可配置循环次数)。一个典型的Proguard处理过程如下:

Proguard处理过程

其中,app classes包括application工程、sub project工程、外部依赖aar/jar、local jar、flat dir aar中的所有java代码。library classes则包括android framework jar、legacy jars等仅在编译期需要的代码,运行时由系统提供,不会打包到apk中。

配置项

Proguard提供了强大的配置项,对整个处理过程进行定制。在这里,将其划分为全局性配置,以及keep配置两类。注意,R8为了保持处理过程的一致可控性,以及更好的处理效果,取消了对大部分全局性配置的支持。

1全局性配置

全局性配置,是指影响整体处理过程的一些配置项,一般又可以分为以下几类:

1、裁剪配置

  • -dontshrink。指定后,关闭裁剪功能;
  • -whyareyoukeeping。指定目标类、变量、方法,为什么被“keep住”,而没有在apk中被裁剪掉。注意,R8和Proguard给出的结果含义并不相同。来直观看下对比:
# 示例:类TestProguardMethodOnly被keep规则直接“keep住”,TestProguardMethodOnly中的一个方法中,调用了TestProguardFieldAndMethod类中的方法。

# Proguard给出的结果,是最短路径,即如果多个keep规则/引用导致,只会给出最短路径的信息
Explaining why classes and class members are being kept...

com.example.myapplication.proguard.TestProguardMethodOnly
  is kept by a directive in the configuration.

com.example.myapplication.proguard.TestProguardFieldAndMethod
  is invoked by    com.example.myapplication.proguard.TestProguardMethodOnly: void methodAnnotation() (13:15)
  is kept by a directive in the configuration.
# 结果解读: 
# 1. “is kept by a directive in the configuration.”,TestProguardMethodOnly是被keep规则直接“keep住”
# 2. “is invoked by xxxx",TestProguardFieldAndMethod是被TestProguardMethodOnly调用,导致被“keep住”;“is kept by a directive in the configuration.”,TestProguardMethodOnly被keep规则直接“keep住”


# R8给出的结果,是类被哪个keep规则直接命中,即如果类被其他保留下来的类调用,但是没有keep规则直接对应此类,那么此处给出的结果,是“Nothing is keeping xxx"
com.example.myapplication.proguard.TestProguardMethodOnly
|- is referenced in keep rule:
|  /Users/flyeek/workspace/code-lab/android/MyApplication/app/proguard-rules.pro:55:1
Nothing is keeping com.example.myapplication.proguard.TestProguardFieldAndMethod
# 结果解读: 
# 1. “is referenced in keep rule: xxx”,TestProguardMethodOnly是被具体的这一条规则直接“keep住”。不过,如果有多条规则均“keep住”了这个类,在此处只会显示一条keep规则。
# 2. “Nothing is keeping xxxx",TestProguardFieldAndMethod没有被keep规则直接“keep住”

2、优化配置

  • -dontoptimize。指定后,关闭优化功能;
  • -optimizationpasses。优化次数,理论上优化次数越多,效果越好。一旦某次优化后无任何效果,将停止下一轮优化;
  • -optimizations。配置具体优化项,具体可参考Proguard文档。下面是随手找的一个proguard处理过程log,大家感受下优化项:

优化(optimize)项展示

  • 其它。包括-assumenosideeffects、-allowaccessmodification等,具体可参考文档,不再详述;

3、混淆配置

  • -dontobfuscate。指定后,关闭混淆功能;
  • 其它。包括-applymapping、-obfuscationdictionary、-useuniqueclassmembernames、dontusemixedcase
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值