提到Proguard,做Android的小伙伴想必是耳熟能详的,它虽然不是由Google开发维护的,Google却将其内置在了Android开发的SDK包中,在编译过程中起到了压缩、优化、混淆Android代码的作用,可以说是Android开发必不可少的一个工具。

Proguard做了什么

如下图所示,在Android应用源码的编译过程中,Proguard将Java bytecode转化为了Optimized Java bytecode,也就是说,Proguard起到了优化Java字节码的作用 。

Proguard优化Java字节码的过程可以分为这四个步骤:shrink(压缩)optimize(优化)obfuscate(混淆)preverify(预校验),如下图所示。

  • Shrink(压缩)

    • 根据设置的EntryPoint(入口点),遍历每个Java字节码文件,确定哪些类及类成员会被程序使用到,不会被使用的则直接丢弃。
    • EntryPoint(入口点)是Proguard中非常关键的一个概念,它定义了Proguard整个优化流程的入口,通常是由-keep系列的配置来指定的,Shrink、Optimize、obfuscate操作都与EntryPoint紧密关联。
    • Proguard只会丢弃不被使用的Java字节码,开发者通常还会搭配Android Gradle插件提供的shrinkResources功能来对不被使用的资源文件进行丢弃。
  • Optimize(优化)
    • Optimize时深入到Java字节码命令的层次进行优化。
    • class维度的优化,必要时增加final标记、做枚举类拆箱(转为整数常量)、做类合并。
    • field维度的优化,必要时增加private标记、移除write-only字段、方法中直接传递字段值。
    • method维度的优化,必要时增加privatestaticfinal标记、去除synchronized标记、移除没用到的参数、直接传递参数的值而不是引用、直接返回结果值而不是引用、将方法(较短的方法、或调用次数少的方法)内联到调用方中、尾部递归简化。
    • code维度的优化,必要时合并不同分支下相同的代码块,使用窥孔优化对变量、属性的存储加载、算术指令、类型转换、分支指令、常量字符、对象实例化进行优化。基于控制流和数据流分析,移除无效的代码、空捕捉的异常、内存占用等等。
  • Obfuscator(混淆)
    • Obfuscator时会对非EntryPoint的类、类成员做重命名,默认情况下它们会被命名为简短无意义的单个或多个的英文字母。
  • Preverify(预校验)
    • 对于Android开发场景,该步骤可以直接跳过。

Proguard与R8

R8的出现

2019年Google在发布的Android Gradle插件3.3.0版本中,提到了可以替代Proguard进行代码压缩和混淆的新工具R8,开发者可以通过在grale.properties文件中添加以下配置启用R8。

android.enableR8 = true

如下图所示,在使用R8时,Android代码的编译步骤四步变为了三步,R8将Proguard和D8做的事情合并为了一步,因此很大程度上可以认为R8相当于Proguard + D8。

下图是R8与Proguard在处理时间、产出包大小上的对比图,可以看出使用R8后,从Java字节码到Dex字节码的构建时间有了较大提升,在产出包的体积方面则和使用Proguard相差不大。

在后来发布Android Gradle插件3.4.0版本中,R8变为默认启用状态,也就是说,开发者只要升级Android Gradle插件到3.4.0,在打包时使用的优化器就不再是Proguard而是R8了,当然,google也给开发者保留了继续使用Proguard的方法,在grale.properties文件中,根据自身需要,选择添加以下配置中的一条则可以继续使用Proguard。

# Disables R8 for Android Library modules only.
android.enableR8.libraries = false
# Disables R8 for all modules.
android.enableR8 = false

Proguard VS R8

如果仅看上面,R8似乎各方面都比Proguard要好,可能有人会想,既然如此那还学proguard作甚,直接R8就完事儿了呗!但事实真的是这样吗?这里简单的对比一下Proguard与R8各自的优缺点。

  • 就应用的构建速度来看,R8更快。

    • R8的构建时间之所以比Proguard更短,最重要的原因是因为它合并了优化、Dex编译两个步骤,免去了中间重新对所有文件遍历读写的过程。如果只是比较编译AAR包(不含D8步骤)的时间,R8将丧失它的优势。
  • 就产出包的压缩率来看,R8略优。

  • 就支持的优化项来看,Proguard支持的更多。

    • 想详细了解的小伙伴,可以看这篇文章:https://www.guardsquare.com/en/blog/comparison-proguard-vs-r8-october-2019-edition。
  • 就稳定性来看,Proguard更可靠。

    • 毕竟Proguard已经走过了15年,在数百万的应用和开发者中形成了一个良性的反馈。而R8自开放以来也就1年多,并且还在进行大量的开发和功能扩展工作。
  • 在使用文档方面,Google目前只在AndroidStudio的官方使用文档中提及到了R8的一些理念和使用方法,至于具体的配置项,R8完全基于Proguard的配置规则,文档也是完全依赖Proguard官方的配置文档。

  • 就受众来说,R8的受众只是Android开发者,而Proguard在设计时的受众是所有Java、Kotlin开发者,只不过它因为内置在Android SDK包中,被众多Android开发者所熟知。

由于R8的配置项几乎完全套用了Proguar中定义的规范,因此Proguard与R8之间的切换是非常顺滑的,作为一名Android开发者,不论你使用Proguard还是R8来做字节码优化,Proguard都是你必须学习和掌握的。

Proguard小知识

要想Proguard用的好,熟读配置少不了,关于配置项的知识建议大家直接去官方文档学习,这里只介绍一些比较重要的或者使用时的注意项。

keep配置项

keep可以说是Proguard配置文件中最常见的一个配置项了,使用keep配置时一定要选择合适的keep级别。

  • -keep

    • 保护类和类成员,不被移除、不被重命名
  • -keepnames
    • 保护类和类成员,不被重命名
  • -keepclassmembers
    • 保护类成员,不被移除、不被重命名
  • -keepclassmembernames
    • 保护类成员,不被重命名
  • -keepclasseswithmembers
    • 保护含有指定成员的类和类成员,不被移除、不被重命名
  • -keepclasseswithmembernames
    • 保护含有指定成员的类和类成员,不被重命名

配置文件合并

说到Proguard配置文件,首先想到的肯定是<module-dir>/proguard-rules.pro这个文件,但其实这只是Android使用的所有Proguard配置的冰山一角罢了。为了Android开发者能更方便地使用Proguard,Android IDE会通过插件自动生成一些通用的Proguard配置放在其他配置文件中,最终在运行Proguard时,将所有这些配置文件中的配置项进行合并。

那除了<module-dir>/proguard-rules.pro,实际上还有哪些Proguard配置文件呢,这里介绍一下:

  • 由Android Gradle插件引入的proguard-android.txtproguard-android-optimize.txt

    • 在新建一个module时,它的build.gradle文件中总会包含这样一段配置:
    proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    

    意思是将使用两个proguard规则文件,第一个配置文件是getDefaultProguardFile('proguard-android-optimize.txt'),如果进入getDefaultProguardFile方法查看,会发现它指向了Android SDK目录下的tools/proguard/proguard-android-optimize.txt文件,第二个配置文件其实就是上面提到的<module-dir>/proguard-rules.pro

        public File getDefaultProguardFile(String name) {File sdkDir = sdkHandler.getAndCheckSdkFolder();return new File(sdkDir,SdkConstants.FD_TOOLS + File.separatorChar+ SdkConstants.FD_PROGUARD + File.separatorChar+ name);}
    
    • proguard-android.txtproguard-android-optimize.txt两个文件中都包含了对大多数 Android 项目有用的规则,proguard-android-optimize.txt只是在proguard-android.txt的基础上打开了Optimize功能并配置了一些Optimize规则,因此如果要使用Optimize功能请使用proguard-android-optimize.txt
  • 由依赖的aar包引入的<library-dir>/proguard.txt、由依赖的jar包引入的<library-dir>/META-INF/proguard/

    • 依赖库中包含的proguard配置文件的内容,实际上是开发Library时,在consumerProguardFiles指定的文件中定义的,用来帮助开发者在使用该库时自动处理该库的proguard配置。
    android {defaultConfig {minSdkVersion 14targetSdkVersion 29versionCode 1versionName "1.0"//在consumer-rules.pro文件中设置,依赖该库时会自动应用的proguard配置consumerProguardFiles 'consumer-rules.pro'}}
    
  • AAPT2工具生成的 <module-dir>/build/intermediates/proguard-rules/debug/aapt_rules.txt

    • aapt2主要负责在构建时解析资源文件,在解析的同时也会生成Proguard配置文件。例如解析AndroidManifest.xml文件时,将其中注册的四大组件全部生成keep配置,解析res/layout下的布局文件时,将其中用到的view组件生成keep配置等等。
  • 开发者通过<module-dir>/build.gradle中的proguardFile设置增加的自定义配置文件,如下所示,增加了一个aaa.txt配置文件。

buildTypes {release {minifyEnabled true//增加一个aaa.txt作为proguard配置文件proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro', 'aaa.txt'}
}

技巧与建议

  • 为了达到最好的优化效果,建议只配置最小的keep维度,不要过度keep。

  • aidl中的所有方法,都要记得做好keep。

  • 所有运行时动态加载相关的代码,如反射,都要记得做好keep。

    • proguard的官网提到了proguard能够自动识别和处理一些反射相关的代码场景,不需要人为配置,但试用下来感觉并不全面,建议大家还是手动配置最好。
  • 在线上应用出错时,往往需要把线上收集的混淆过的错误栈信息,对照着mapping文件翻译为可读的栈信息,这个过程其实可以借助Proguard提供的retrace命令帮我们自动完成的。

    1. 假设应用抛出了以下异常信息
    java.io.IOException: Can't read [dummy.jar] (No such file or directory)at proguard.y.a(MyApplication:188)at proguard.y.a(MyApplication:158)at proguard.y.a(MyApplication:136)at proguard.y.a(MyApplication:66)at proguard.ProGuard.c(MyApplication:218)at proguard.ProGuard.a(MyApplication:82)at proguard.ProGuard.main(MyApplication:538)
    Caused by: java.io.IOException: No such file or directoryat proguard.d.q.a(MyApplication:50)at proguard.y.a(MyApplication:184)... 6 more
    
    1. 可以将异常堆栈信息保存到stacktrace.txt文件中,运行以下命令(retrace命令在proguard包的bin目录下)
    retrace mapping.txt stacktrace.txt
    
    1. 运行成功,可以看到输出了可读的堆栈信息
    java.io.IOException: Can't read [dummy.jar] (No such file or directory)    at proguard.InputReader.readInput(InputReader.java:188)    at proguard.InputReader.readInput(InputReader.java:158)    at proguard.InputReader.readInput(InputReader.java:136)    at proguard.InputReader.execute(InputReader.java:66)    at proguard.ProGuard.readInput(ProGuard.java:218)    at proguard.ProGuard.execute(ProGuard.java:82)    at proguard.ProGuard.main(ProGuard.java:538)Caused by: java.io.IOException: No such file or directory    at proguard.io.DirectoryPump.pumpDataEntries(DirectoryPump.java:50)    at proguard.InputReader.readInput(InputReader.java:184)    ... 6 more
    
  • Proguard支持设置混淆时使用的字符字典,并不是只能混淆为’a’, ‘b’, 'c’这种。

# 设置用于混淆field,method名称的字典-obfuscationdictionary Tibet.txt# 设置用于混淆类名称的字典-classobfuscationdictionary Tibet.txt# 设置用于混淆包名的字典-packageobfuscationdictionary Tibet.txt

例如,这里定义一个Tibet.txt字典文件,其中定义了一些藏语字符。

使用该字典的混淆效果如下所示

  • 多Module工程中使用Proguard时的建议和注意事项

    • 主Module中Proguard的开关会直接影响到子Module。只要主Module中的proguard开关是开的,那么即使子Module中的proguard开关是关闭的,在执行主Module的打包命令时,仍然会对子Module做Proguard操作。
    • 建议各个Module自行维护自己的Proguard配置文件,避免所有Model全都配置在同一个文件中。
  • porguard-rules.pro文件 和 consumer-rules.pro文件

    • proguard-rules.pro文件一般指的是新建module时proguardFiles指定的默认文件。(当然开发者可以换成别的文件名)

    • consumer-rules.pro文件一般指的是一个在Library类型的Module中才会出现的配置文件,它是新建Library类型的Module时consumerProguardFiles指定的默认文件。(当然开发者也可以换成别的文件名)

    • porguard-rules.pro文件 和 consumer-rules.pro的区别在于

      • 在打包当前Module时,Proguard会用到该Module下的proguard-rules.pro文件中的配置项对其进行优化,打包完成后该文件不会出现在产出的包中。
      • consumer-rules.pro文件在Library Module打包成AAR或Jar时,仍然会在包中保留该文件。
      • 在打包其他用到该Library Module(只能是Library类型)或AAR/Jar包的Module时,Proguard在处理该Library Module时,会用到该Library Module或AAR/Jar包提供的consumer-rules.pro文件中的配置项进行优化。
  • 在开发Library时,尽量不要在consumer-rules.pro中设置如dontoptimize这样的配置项,因为Android在打包时会合并所有的配置文件,如果在Library中设置了关闭optimize,那么所有使用该Library的Module都将关闭optimize。如果Library一定要关闭optimize,请在Library下的proguard-rules.pro文件中设置,并将Library打包成AAR/Jar包后,再给其他Module使用。

Proguard总结

  • Proguard在Android Application/Library的编译过程中,起到了Shrink(压缩)、Optimize(优化)、Obfuscate(混淆) Java字节码的作用。
  • 虽然Google开发了R8来替代Proguard,但R8基本完全套用了Proguard的配置,因此Proguard仍然是Android开发者必须掌握的。
  • Android IDE为了方便Android开发者使用Proguard做了很多自动化管理,开发者通常只需要关注与自身项目相关的Proguard配置即可。
  • Proguard只会处理项目中的Java字节码部分,对于资源文件的优化,可以通过Android Gradle插件提供的shrinkResources配置移除没有用到的资源,通过AndResGuard Gradle插件对资源文件进行压缩和混淆。

Proguard那些事儿相关推荐

  1. 关于proguard的使用总结

    在聊proguard使用之前,先说说proguard到底是什么东东,我主要做android开发,平时一般都听过android混淆打包的说法,直观的感觉就是把写好的java代码,通过一种编码方式给混淆了 ...

  2. webassembly类型_WebAssembly 那些事儿

    WebAssembly 那些事儿 什么是 WebAssembly? WebAssembly 是除 JavaScript 以外,另一种可以在网页中运行的编程语言,并且相比之下在某些功能和性能问题上更具优 ...

  3. android类名方法名不混淆,android – 如何告诉Proguard混淆类名

    我想用proguard来混淆类名.我在Proguard.cfg中有这一行 -keepclasseswithmembers class * { public static ; } -keepnames ...

  4. 计算机网络技术社团纳新海报,精品社团纳新 | 加入计算机协会和我一起做些有意义的事儿吧~...

    原标题:精品社团纳新 | 加入计算机协会和我一起做些有意义的事儿吧~ 我们是谁? 计算机协会 社团 简介 社团名称:计算机协会 创办时间:2012年 组成部门:技术部,宣传部,秘书部,外联部,策划部. ...

  5. Linux那些事儿 之 戏说USB(33)字符串描述符

    关于字符串描述符,前面的前面已经简单描述过了,地位仅次于设备/配置/接口/端点四大描述符,那四大设备必须得支持,而字符串描述符对设备来说则是可选的. 这并不是就说字符串描述符不重要,对咱们来说,字符串 ...

  6. Linux那些事儿 之 戏说USB(28)设备的生命线(十一)

    现在已经使用GET_DESCRIPTOR请求取到了包含一个配置里所有相关描述符内容的一堆数据,这些数据是raw的,即原始的,所有数据不管是配置描述符.接口描述符还是端点描述符都彼此的挤在一起,所以得想 ...

  7. Linux那些事儿 之 戏说USB(27)设备的生命线(十)

    跟着设备的生命线走到现在,我算是明白了,什么东西的发展都是越往后越高级越复杂.再给张小表,看看现在和上次那张表出现的时候有什么变化. state        USB_STATE_ADDRESS sp ...

  8. Linux那些事儿 之 戏说USB(25)设备的生命线(八)

    回到struct usb_hcd,继续努力的往下看. 7行,又见kref,usb主机控制器的引用计数.struct usb_hcd也有自己专用的引用计数函数,看drivers/usb/core/hcd ...

  9. Linux那些事儿 之 戏说USB(22)设备的生命线(五)

    下面接着看那三个基本点. 第一个基本点,usb_alloc_urb函数,创建urb的专用函数,为一个urb申请内存并做初始化,在drviers/usb/core/urb.c里定义. struct ur ...

最新文章

  1. 制作血条_unity-UGUI如何制作血条
  2. 【转】教你何时开启水果机上的HDR拍照
  3. Tcp与Ip协议的客户端和服务器编程
  4. 基于阿里云物联网平台,我们这样实现简易出入监控
  5. 【调试工具】之Python调试工具pycharm
  6. 数据库存储I/O类型分析与配置
  7. 使用PDF-XChange Editor为PDF文件添加图章(仅图片)
  8. SQL书写规则30例
  9. VMware之虚拟机迁移
  10. 行政区划信息抽取算法(区划抽取)
  11. 文员常用的8个excel函数
  12. 巴菲特致股东的一封信:1991年
  13. filp_open/filp_close/vfs_read/vfs_write
  14. 我做了一款iOS12捷径市场,也许是目前最好看也是最具技术含量的ShortCuts小程序
  15. Springboot项目install打包-某些输入文件使用了未经检查或不安全的操作。分析与解决
  16. 英文论文写作摘要的时态和语态
  17. IDEA调试遇到Method breakpoints may dramatically slow down debugging
  18. 使用Keras进行深度学习(二): CNN讲解及实践
  19. QuaterDeck 什么鬼意思?
  20. OTT渠道首次反超PC,2022互联网营销结构的拐点又来了?

热门文章

  1. matlab三斜晶系,高分子与计算机模拟
  2. halcon学习和实践(工业视觉套路)
  3. PatchwerkHeal
  4. MAC/Linux 压缩/解压缩命令大全整理 gzip / tar / zip
  5. PHP-SOCKETS编程问题
  6. 2022最新解决APK报毒的程序源码+安装教程
  7. 华硕X55笔记本更换SSD硬盘
  8. 小程序input组件获得焦点时placeholder内容有重影
  9. 计算机辅助教学:多媒体课件制作教程,计算机辅助教学——多媒体课件制作教程 教学课件 作者 付明柏电子教案( ) 第7章 利用Dreamweaver制作多媒体CAI课件.ppt...
  10. 两个小概念 Hot swap和Hot spare