Android实践:使用ProGuard压缩和混淆Apk

本文主要讨论对apk文件的压缩和混淆中的细节问题以及分析在压缩混淆过程中遇到的问题的原因.

ProGuard压缩混淆Java代码

ProGuard的用法和自定义规则可以参考Android Proguard(混淆), 本文不作陈列说明.

压缩的意思是删除没有被直接使用的类和类成员(包括fields和methods).
混淆的意思是将类或者类成员重命名为不规则的名字, 通常是字母.

注意点

1. ProGuard仅可处理Java类文件

ProGuard针对的是Java类文件(Java class file), 所以不能处理非Java类文件, 例如xml文件, 图片文件.

2. Android中ProGuard作用

ProGuard提供4个功能, 压缩(shrinker), 优化(optimizer), 混淆(obfuscator)预校验(preverifier), 但是在Android中默认会关闭优化和预校验功能.
官方文档的解释是

Optimization is turned off by default. Dex does not like code runthrough the ProGuard optimize and preverify steps (and performs some of these optimizations on its own).

3. SDK相关的混淆处理

虽然没有找到相关的说明, 但是在Android Studio 2.2, Android Plugin是2.2.0的情况下, 开启minifyEnabled true但是不使用proguardFiles提供任何配置文件, 仍然会正常压缩混淆代码, 并且会保留Activity, Keep等SDK类. 但是如果你提供自己的配置文件, 那么记得加上getDefaultProguardFile('proguard-android.txt')

4. -injars, -outjars-libraryjars

-injars : 指定需要经过ProGuard处理的文件
-outjars : 指定经过处理后输出的文件名
-libraryjars : 指定不需要经过ProGuard处理的文件
这3个命令不会在Android中用到, 因为Andorid Plugin会自动将引用的库加入到injars中.

5. -keep指令

具体的用法还是建议看文档, 戳这里
关键格式

-keep [,modifier,...] class_specification

值得注意的点

  1. modifier中可以使用includedescriptorclasses参数来保护在类成员提到的类不被混淆, 例如指定保护了方法void method(Param p), 如果不带这个参数, Param是可以被混淆的(没有其他设置指明保护它的时候), 使用这个参数则可以防止Param被混淆, 具体描述看文档
  2. class_specification中, 类和类成员是两种描述对象, 就是说可以仅仅保护类但是不保护其中的类成员, 例如-keep public class com.sample.A, 这里仅仅指定了类, 所以类A不会被删除或者被混淆, 但是A里面的fields和methods则可以被删除和被混淆; 再例如-keep public class com.sample.A{*;}, 这里不仅保护A类, 而且保护A类里面所有类成员(包括fields和methods)不被删除和混淆.
  3. class关键字是包含了类和接口的
  4. 指明方法的时候具体的参数和返回值的类型是必须指定的, 可以使用***来匹配任意参数类型

6. -keep-keepnames的区别

-keep的意思是符合条件的类和类成员既不会被压缩也不会被混淆
-keepnames-keep,allowshrinking的缩写, 而allowshrinking的意思是允许符合条件的类和类成员被压缩(删除)

7. 使用@Keep保护特定类和类成员

如果引入了android.support.annotation库可以使用@Keep来在代码中保护指定的类和类成员.
实践效果

  1. 放到类前, 会保护类和所有类成员, 相当于-keep class A {*;}
  2. 放到类成员前, 会保护类和对应的类成员, 相当于-keepclassmembers class A {fieldType fieldName;}
  3. 放在Method前时, 不会保护参数不被混淆

8. AndroidManifest.xml中使用的类

不添加额外的配置, 仅使用默认的配置, 测试结果是会保护在AndroidManifest.xml直接使用的类和继承过来的类成员, 但是不会保护添加的类成员

9. R文件

app的默认编译过程会把R文件的引用转成具体的值, 例如如果R.layout.activity_main = 1, 那么所有用到R.layout.activity_main的地方都会用1代替, 然后R文件不会包含到apk中, 因此如果有通过字符串获取资源文件, 则需要手动保护R文件
NOTE: 从一些地方看到的说法是"R文件有可能不会被包含进apk"

ProGuard QA

Q: 在Android优化中使用-libraryjars报错

Warning:Exception while processing task java.io.IOException: The same input jar some.jar is specified twice.

注: 支付宝移动支付sdk的混淆配置
A: 因为在build.gradle中声明依赖关系的时候一般会通过compile命令声明编译某个jar包, 如

compile fileTree(include: '*.jar', dir: 'libs')

编译jar包相当于-injars命令, 而这两个命令是冲突的, 所以只要在依赖关系中引入了某个jar包就不能再对该jar包使用-injars或者-libraryjars命令

Q: 报红色warning, 提示各种InnerClasses或者EnclosingMethod****
A:
网上所有建议都是添加-keepattributes InnerClasses,EnclosingMethod, 但是添加之后可以消除一些, 还是会有, 不过不影响编译.
NOTE: 查阅一些资料之后我推测是因为SDK编译时候使用的JDK版本的原因导致这些问题, 但是不能确认

Q: ProGuard文档中的Entry Point的意思****
A:
使用-keep可以使指定的类和类成员成为Entry Point, 其实就是扫描开始的地方, 即使没有其他人直接引用这个类或者类成员, 也保留它.

Android中ProGuard的优化结果

优化结果文件会输出到<module-name>/build/outputs/mapping/release/目录.

  1. dump.txt : 描述APK中所有类文件的内部结构(internal structure)
  2. mapping.txt : 提供混淆前后类(class)名, 方法(method)名和成员变量(field)名的对应关系
  3. seeds.txt : 列出没有被混淆的类和成员(classes and members)
  4. usage.txt : 列出从APK中移除的代码(code)

分析混淆后的报错信息可以参考这篇文章 android-how-to-decode-proguards-obfuscated-code-from-stack-trace, 其实有个带界面的小工具来分析报错信息的.

Resource shrinking压缩资源文件

以下内容都来自Shrink Your Resources
ProGuard不能压缩资源文件, 所以在Android中是使用Gradle的Androidd插件中的Resource shrinking来移除没有被使用的资源文件的, 包括库文件内的资源文件. 它会在ProGuard压缩之后运行, 所以被没有使用的类引用的资源文件也会被删除.

通过

shrinkResource true
minifyEnabled true // 开启ProGuard是前提条件

开启压缩资源文件
NOTE: Resource shrinking暂时(2016/11/2)不会对res/values内的文件进行压缩

自定义规则

创建一个包含<resources>的xml文件放到资源目录, 通过tools:keep指定保留的资源文件, 通过tools:discard指定明确删除的资源文件. 指定资源文件时通过,分隔, 通过*匹配任意字符. 例如

xmltools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"

NOTE: 官方文档中没有说该文件需要特定的文件名和路径, 仅举例res/raw/keep.xml. 该文件不会被放进最后的apk文件中.

被动态使用的资源

Android中可以通过[Resources.getIdentifier()](https://developer.android.com/reference/android/content/res/Resources.html#getIdentifier(java.lang.String, java.lang.String, java.lang.String))来通过字符串匹配来动态获取某个资源文件id, 当使用了这个方法时(v7 appcompat library中使用了), Resource shrinking不会压缩符合字符串规则的文件.

可以通过tools:shrinkMode="strict"来关闭这个默认行为, 指明只有明确被引用的资源才保留, 代码中动态引用的资源则默认不保留.

Resource shrinking压缩结果

资源文件压缩后可以通过<module-name>/build/outputs/mapping/release/resources.txt来查看所有资源文件的关系.

aar库包含混淆规则

参考生成带混淆配置的aar库
关键属性是consumerProguardFiles
库的build.gradle中配置类似

android {
   defaultConfig {
      minifyEnabled true
      consumerProguardFiles 'consumer-proguard-rules.pro'
   }
}

注意, 因为启用了minifyEnabled, 因此编译库时会混淆整个lib, 这样会导致外部工程不能正常引用库中的类, 因为类名被混淆了, 所以需要添加混淆规则, 确保库暴露给外部的API相关类不被混淆

END

这边文章的主要目的是分析ProGuard优化Android代码时遇到的问题, 欢迎在讨论区指出不明白的地方或者提出你遇到的问题, 大家一起研究.

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

推荐阅读更多精彩内容