美文网首页Android进阶Android实践今日看点
Android实践:使用ProGuard压缩和混淆Apk

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

作者: AssIstne | 来源:发表于2016-11-02 16:41 被阅读1922次

本文主要讨论对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代码时遇到的问题, 欢迎在讨论区指出不明白的地方或者提出你遇到的问题, 大家一起研究.

相关文章

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

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

  • 15 性能优化-Apk瘦身-代码混淆和资源压缩

    Android apk瘦身最佳实践(二):代码混淆和资源压缩 要尽可能减小 APK 文件,我们应该启用压缩来移除发...

  • Android混淆详解

    前言 混淆是上线前挺重要的一个环节。android使用的ProGuard,可以起到压缩,混淆,预检,优化的作用。但...

  • 5分钟搞定android混淆

    前言 混淆是上线前挺重要的一个环节。Android使用的ProGuard,可以起到压缩,混淆,预检,优化的作用。但...

  • 5分钟搞定android混淆

    前言 混淆是上线前挺重要的一个环节。android使用的ProGuard,可以起到压缩,混淆,预检,优化的作用。但...

  • Android开启压缩代码和资源(混淆)

    什么是混淆 代码压缩通过 ProGuard 提供,ProGuard 会检测和移除封装应用中未使用的类、字段、方法和...

  • Android开发之代码混淆

    应用混淆(ProGuard) ProGuard是一个免费的JAVA类文件压缩,优化,混淆器。 它探测并删除没有使用...

  • ProGuard代码混淆详细攻略

    ProGuard简介和工作流程 ProGuard能够通过压缩、优化、混淆、预检等操作,检测并删除未使用的类,字段,...

  • Android混淆工具——Proguard实践

    最近使用了一个非常高效和方便的混淆工具——Proguard,使用了这个工具混淆打包后,apk体积显著的减少了,而且...

  • Android 混淆

    混淆 studio 使用Proguard进行混淆,其是一个压缩、优化和混淆java字节码文件的一个工具。 功能:S...

网友评论

    本文标题:Android实践:使用ProGuard压缩和混淆Apk

    本文链接:https://www.haomeiwen.com/subject/ljnvuttx.html