Android 项目代码混淆初探

文章目录

前景:

作为Android开发者,如果你不想开源你的应用,那么在应用发布前,就需要对代码进行混淆处理,从而让我们代码即使被反编译,也难以阅读。除了防止源码泄露,进行代码混淆还可以起到减少app体积的作用。

简介

混淆不仅仅是指把类名、属性名、方法名替换为简短且无意义的名称,主要包含四大方面:

代码缩减(即摇树优化):从应用及其库依赖项中检测并安全地移除不使用的类、字段、方法和属性(这使其成为了一个对于规避 64k 引用限制非常有用的工具)。例如,如果您仅使用某个库依赖项的少数几个 API,那么缩减功能可以识别应用不使用的库代码并仅从应用中移除这部分代码

资源缩减:从封装应用中移除不使用的资源,包括应用库依赖项中不使用的资源。此功能可与代码缩减功能结合使用,这样一来,移除不使用的代码后,也可以安全地移除不再引用的所有资源。

混淆:缩短类和成员的名称(除非用keep保护),从而减小 DEX 文件的大小。。

优化:检查并重写代码,以进一步减小应用的 DEX 文件的大小。例如,如果 R8 检测到从未采用过给定 if/else 语句的 else {} 分支,则会移除 else {} 分支的代码。如需了解详情,请转到介绍代码优化的部分。

如何开启压缩,混淆,优化功能

当您使用 Android Studio 3.4 或 Android Gradle 插件 3.4.0 及更高版本时,R8 是默认编译器,用于将项目的 Java 字节码转换为在 Android 平台上运行的 DEX 格式。不过,当您使用 Android Studio 创建新项目时,缩减、混淆处理和代码优化功能默认处于停用状态。这是因为,这些编译时优化功能会增加项目的构建时间,而且如果您没有充分自定义要保留的代码,还可能会引入错误。

因此,在构建应用的最终版本(也就是在发布应用之前测试的版本)时,最好启用这些编译时任务。如需启用缩减、混淆处理和优化功能,请在项目级 build.gradle 文件中添加以下代码。

android {
    buildTypes {
        release {
            // Enables code shrinking, obfuscation, and optimization for only
            // your project's release build type.
            minifyEnabled true

            // Enables resource shrinking, which is performed by the
            // Android Gradle plugin.
            shrinkResources true

            // Includes the default ProGuard rules files that are packaged with
            // the Android Gradle plugin. To learn more, go to the section about
            // R8 configuration files.
            proguardFiles getDefaultProguardFile(
                    'proguard-android-optimize.txt'),
                    'proguard-rules.pro'
        }
    }
    ...
}

ProGuard规则不仅仅来自于自己项目自定义的proguard-rules.pro文件,还某些规则可能由编译时工具(如 AAPT2)自动生成,或从应用的库依赖项继承而来

可查看R8配置文件

代码缩减

如果将 minifyEnabled 属性设为 true,系统会默认启用 R8 代码缩减功能。

代码缩减(也称为“摇树优化”)是指移除 R8 确定在运行时不需要的代码的过程。此过程可以大大减小应用的大小,例如,当您的应用包含许多库依赖项,但只使用它们的一小部分功能时。

为了缩减应用的代码,R8首先会根据组合的配置文件集确定应用代码的所有入口点。这些入口点包括 Android 平台可用来打开应用的 Activity 或服务的所有类。从每个入口点开始,R8会检查应用的代码来构建一张图表,列出应用在运行时可能会访问的所有方法、成员变量和其他类。系统会将与该图表没有关联的代码视为执行不到的代码,并可能会从应用中移除该代码。

通俗点讲就是R8通过项目配置文件及自己定义的中保留规则确定入口点,移除未被执行过的代码

资源缩减

如需启用资源缩减功能,请将 build.gradle 文件中的 shrinkResources 属性(若为代码缩减,则还包括 minifyEnabled)设为 true。如果您尚未使用用于缩减代码的 minifyEnabled 构建应用,请先尝试使用它,然后再启用 shrinkResources,因为您可能需要先修改 proguard-rules.pro文件以保留动态创建或调用的类或方法,然后再开始移除资源

自定义保留资源规则(keep.xml)

用shrinkResources true开启资源压缩后,所有未被使用的资源默认被移除。假如你需要定义哪些资源必须被保留,在 res/raw/ 路径下创建一个 xml 文件,例如keep.xml 。

通过一些属性的设置可以实现定义资源保持的需求,可配置的属性有:

关键字 描述
keep 定义哪些资源需要被保留(资源之间用“,”隔开)
discard 定义哪些资源需要被移除(资源之间用“,”隔开)
shrinkMode 开启严格模式

当代码中通过 Resources.getIdentifier() 用动态的字符串来获取并使用资源时,普通的资源引用检查就可能会有问题。例如,如下代码会导致所有以“img_”开头的资源都被标记为已使用。
当代码中通过 Resources.getIdentifier() 用动态的字符串来获取并使用资源时,普通的资源引用检查就可能会有问题。例如,如下代码会导致所有以“img_”开头的资源都被标记为已使用。

String name = String.format("img_%1d", angle + 1);
res = getResources().getIdentifier(name, "drawable", getPackageName());

我们可以设置 tools:shrinkMode 为 strict 来开启严格模式,使只有确实被使用的资源被保留。

以上就是自定义资源保持规则相关的配置,举个例子:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
    tools:discard="@layout/unused2"
    tools:shrinkMode="strict"/>

移除未使用的备用资源

一些替代资源,例如多语言支持的 strings.xml,多分辨率支持的 layout.xml 等,在我们不需要使用又不想删除掉时,可以使用资源压缩将它们移除。

我们使用 resConfig 属性来指定需要支持的属性,例如
一些替代资源,例如多语言支持的 strings.xml,多分辨率支持的 layout.xml 等,在我们不需要使用又不想删除掉时,可以使用资源压缩将它们移除。

我们使用 resConfig 属性来指定需要支持的属性,例如

android {
    defaultConfig {
        ...
        resConfigs "en", "fr"
    }
}

合并重复资源

默认情况下,Gradle 还会合并同名的资源,如可能位于不同资源文件夹中的同名可绘制对象。这一行为不受 shrinkResources 属性控制,也无法停用,因为当多个资源与代码查询的名称匹配时,有必要利用这一行为避免错误。

只有在两个或更多个文件具有完全相同的资源名称、类型和限定符时,才会进行资源合并。Gradle 会在重复项中选择它认为最合适的文件(根据下述优先顺序),并且只将这一个资源传递给 AAPT,以便在 APK 文件中分发。

Gradle 会在以下位置查找重复资源:

与主源代码集关联的主资源,一般位于 src/main/res/ 中。
变体叠加,来自构建类型和构建变种。
库项目依赖项。
Gradle 会按以下级联优先顺序合并重复资源:

依赖项 → 主资源 → 构建变种 → 构建类型

例如,如果某个重复资源同时出现在主资源和构建变种中,Gradle 会选择构建变种中的资源。

如果完全相同的资源出现在同一源代码集中,Gradle 无法合并它们,并且会发出资源合并错误。如果您在 build.gradle 文件的 sourceSet 属性中定义了多个源代码集,就可能会发生这种情况。例如,如果 src/main/res/ 和 src/main/res2/ 包含完全相同的资源,就可能会发生这种情况。

混淆

在上文“混淆配置”中有这样一行代码

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

这行代码定义了混淆规则由两部分构成:位于 SDK 的 tools/proguard/ 文件夹中的 proguard-android.txt的内容以及默认放置于模块根目录的proguard-rules.pro的内容。前者是 SDK 提供的默认混淆文件,后者是开发者自定义混淆规则的地方。

默认混淆配置(proguard-android.txt)

位于 SDK 的 tools/proguard/ 文件夹中的 proguard-android.txt 的内容

# This is a configuration file for ProGuard.
# http://proguard.sourceforge.net/index.html#manual/usage.html
#
# This file is no longer maintained and is not used by new (2.2+) versions of the
# Android plugin for Gradle. Instead, the Android plugin for Gradle generates the
# default rules at build time and stores them in the build directory.

-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose

# Optimization is turned off by default. Dex does not like code run
# through the ProGuard optimize and preverify steps (and performs some
# of these optimizations on its own).
-dontoptimize
-dontpreverify
# Note that if you want to enable optimization, you cannot just
# include optimization flags in your own project configuration file;
# instead you will need to point to the
# "proguard-android-optimize.txt" file instead of this one from your
# project.properties file.

-keepattributes *Annotation*
-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService

# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
-keepclasseswithmembernames class * {
    native <methods>;
}

# keep setters in Views so that animations can still work.
# see http://proguard.sourceforge.net/manual/examples.html#beans
-keepclassmembers public class * extends android.view.View {
   void set*(***);
   *** get*();
}

# We want to keep methods in Activity that could be used in the XML attribute onClick
-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}

# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

-keepclassmembers class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator CREATOR;
}

-keepclassmembers class **.R$* {
    public static <fields>;
}

# The support library contains references to newer platform versions.
# Don't warn about those in case this app is linking against an older
# platform version.  We know about them, and they are safe.
-dontwarn android.support.**

# Understand the @Keep support annotation.
-keep class android.support.annotation.Keep

-keep @android.support.annotation.Keep class * {*;}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <methods>;
}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <fields>;
}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <init>(...);
}

配上中文解读:
-dontusemixedcaseclassnames 表示混淆时不使用大小写混合类名。

-dontskipnonpubliclibraryclasses 表示不跳过library中的非public的类。
-verbose 表示打印混淆的详细信息。

-dontoptimize 表示不进行优化,建议使用此选项,因为根据proguard-android-optimize.txt中的描述,优化可能会造成一些潜在风险,不能保证在所有版本的Dalvik上都正常运行。

-dontpreverify 表示不进行预校验。这个预校验是作用在Java平台上的,Android平台上不需要这项功能,去掉之后还可以加快混淆速度。

-keepattributes Annotation 表示对注解中的参数进行保留。

-keep public class com.google.vending.licensing.ILicensingService

-keep public class com.android.vending.licensing.ILicensingService

表示不混淆上述声明的两个类,这两个类我们基本也用不上,是接入Google原生的一些服务时使用的。

-keepclasseswithmembernames class * {
    native <methods>;
}

表示不混淆任何包含native方法的类的类名以及native方法名,这个和我们刚才验证的结果是一致的。

-keepclassmembers public class * extends android.view.View {
   void set*(***);
   *** get*();
}

表示不混淆任何一个View中的setXxx()和getXxx()方法,因为属性动画需要有相应的setter和getter的方法实现,混淆了就无法工作了。

-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}

表示不混淆Activity中参数是View的方法,因为有这样一种用法,在XML中配置android:onClick="buttonClick"属性,当用户点击该按钮时就会调用Activity中的buttonClick(View view)方法,如果这个方法被混淆的话就找不到了。

-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

表示不混淆枚举中的values()和valueOf()方法,枚举我用的非常少,这个就不评论了。

-keepclassmembers class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator CREATOR;
}

表示不混淆Parcelable实现类中的CREATOR字段,毫无疑问,CREATOR字段是绝对不能改变的,包括大小写都不能变,不然整个Parcelable工作机制都会失败。

-keepclassmembers class **.R$* {
    public static <fields>;
}

表示不混淆R文件中的所有静态字段,我们都知道R文件是通过字段来记录每个资源的id的,字段名要是被混淆了,id也就找不着了。

-dontwarn android.support.** 表示对android.support包下的代码不警告,因为support包中有很多代码都是在高版本中使用的,如果我们的项目指定的版本比较低在打包时就会给予警告。不过support包中所有的代码都在版本兼容性上做足了判断,因此不用担心代码会出问题,所以直接忽略警告就可以了。

-keep @android.support.annotation.Keep class * {*;}
表示不混淆 被@android.support.annotation.Keep 修饰的类

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <methods>;
}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <fields>;
}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <init>(...);
}

表示不混淆类中被@android.support.annotation.Keep 修饰的构造函数、方法、字段

保持元素不参与混淆的规则

  [保持命令] [类] {
    [成员] 
}
  • “保持命令”
    proguard中一共有三组六个keep关键字,很多人搞不清楚它们的区别,这里我们通过一个表格来直观地看下:
关键字 描述
keep 保留类和类中的成员,防止它们被混淆或移除。
keepnames 保留类和类中的成员,防止它们被混淆,但当成员没有被引用时会被移除。
keepclassmembers 只保留类中的成员,防止它们被混淆或移除。
keepclassmembernames 只保留类中的成员,防止它们被混淆,但当成员没有被引用时会被移除。
keepclasseswithmembers 保留类和类中的成员,防止它们被混淆或移除,前提是指名的类中的成员必须存在,如果不存在则还是会混淆。
keepclasseswithmembernames 保留类和类中的成员,防止它们被混淆,但当成员没有被引用时会被移除,前提是指名的类中的成员必须存在,如果不存在则还是会混淆。

很多人对于keep和keepclasseswithmembers这两个关键字的区别还是搞不懂。确实,它们之间用法有点太像了,我做了很多次试验它们的结果都是相同的。其实唯一的区别就在于类中声明的成员存不存在,我们还是通过一个例子来直接地看一下,先看keepclasseswithmember关键字:

-keepclasseswithmember class * {
    native <methods>;
}

这段代码的意思其实很明显,就是保留所有含有native方法的类的类名和native方法名,而如果某个类中没有含有native方法,那就还是会被混淆。

但是如果改成keep关键字,结果会完全不一样:

-keep class * {
    native <methods>;
}

使用keep关键字后,你会发现代码中所有类的类名都不会被混淆了,因为keep关键字看到class *就认为应该将所有类名进行保留,而不会关心该类中是否含有native方法。当然这样写只会保证类名不会被混淆,类中的成员还是会被混淆的。

  • “类”代表类相关的限定条件,它将最终定位到某些符合该限定条件的类。它的内容可以使用:

具体的类
访问修饰符(public、protected、private)

通配符*,匹配任意长度字符,但不含包名分隔符(.)

通配符**,匹配任意长度字符,并且包含包名分隔符(.)

extends,即可以指定类的基类

implement,匹配实现了某接口的类

$,内部类

  • “成员”代表类成员相关的限定条件,它将最终定位到某些符合该限定条件的类成员。它的内容可以使用:

匹配所有构造器

匹配所有域

匹配所有方法

通配符*,匹配任意长度字符,但不含包名分隔符(.)

通配符**,匹配任意长度字符,并且包含包名分隔符(.)

通配符*,匹配任意参数类型

…,匹配任意长度的任意类型参数。比如void test(…)就能匹配任意 void test(String a) 或者是 void test(int a, String b) 这些方法。

访问修饰符(public、protected、private)

举个例子,假如需要将com.biaobiao.test包下所有继承Activity的public类及其构造函数都保持住,可以这样写:

 -keep public class com.biaobiao.test.** extends Android.app.Activity {
    <init>

常见自定义混淆规则

  • 不混淆某个类-keep public class com.demo.example.Test { *; }
  • 不混淆某个包所有的类
    -keep class com.demo.test.** { *; }
  • 不混淆某个类的子类
    -keep public class * extends com.demo.example.Test { *; }
  • 不混淆某个接口的实现
    -keep class * implements com.demo.example.TestInterface { *; }
  • 不混淆某个类的构造方法
    -keepclassmembers class com.demo.example.Test {
    public ();
    }
  • 不混淆某个类的特定的方法
    -keepclassmembers class com.demo.example.Test {
    public void test(java.lang.String);
    }
  • 不混淆某个类的内部类
    -keep class com.demo.example.Test$* {
    *;
    }

解码经过混淆处理的堆栈轨迹

R8 对代码进行混淆处理后,理解堆栈轨迹的难度将会极大增加,因为类和方法的名称可能有变化。除了重命名之外,R8 还可能会更改出现在堆栈轨迹中的行号,以便在写入 DEX 文件时进一步缩减大小。幸运的是,R8 在每次运行时都会创建一个 mapping.txt 文件,其中列出了经过混淆处理的类、方法和字段的名称与原始名称的映射关系。此映射文件还包含用于将行号映射回原始源文件行号的信息。R8 会将此文件保存在 /build/outputs/mapping// 目录中。

注意:您每次构建项目时都会覆盖 R8 生成的 mapping.txt 文件,因此您每次发布新版本时都要注意保存一个该文件的副本。通过为每个发布 build 保留一个 mapping.txt 文件的副本,您可以在用户提交来自旧版应用的经过混淆处理的堆栈轨迹时,调试相关问题。

为确保跟踪还原堆栈轨迹清楚明确,您应将以下保留规则添加到模块的.pro文件中:

    -keepattributes LineNumberTable,SourceFile

需要 LineNumberTable 属性,以消除方法内经过优化的位置之间的歧义。如需获取在虚拟机或设备上的堆栈轨迹中输出的行号,则必须使用 SourceFile 属性。

在 Google Play 上发布应用时,您可以上传每个 APK 版本对应的 mapping.txt 文件。然后,Google Play 会根据用户报告的问题对传入的堆栈轨迹进行去混淆处理,以便您可以在 Play 管理中心查看这些堆栈轨迹。如需了解详情,请参阅介绍如何对崩溃堆栈轨迹进行去混淆处理的帮助中心文章。

如需自行解码经过混淆处理的堆栈轨迹,请使用与命令行工具软件包捆绑在一起的 retrace 命令行工具。

代码优化

为了进一步缩减应用,R8 会在更深的层次上检查代码,以移除更多不使用的代码,或者在可能的情况下重写代码,以使其更简洁。下面是此类优化的几个示例:

如果您的代码从未采用过给定 if/else 语句的 else {} 分支,R8 可能会移除 else {} 分支的代码。
如果您的代码只在一个位置调用某个方法,R8 可能会移除该方法并将其内嵌在这一个调用点。
如果 R8 确定某个类只有一个唯一子类且该类本身未实例化(例如,一个仅由一个具体实现类使用的抽象基类),它就可以将这两个类组合在一起并从应用中移除一个类。
如需了解详情,请阅读 Jake Wharton 撰写的关于 R8 优化的博文。
R8 不允许您停用或启用离散优化,也不允许您修改优化的行为。事实上,R8 会忽略试图修改默认优化行为的所有 ProGuard 规则,例如 -optimizations 和 - optimizationpasses。此限制很重要,因为随着 R8 的不断改进,维护标准的优化行为有助于 Android Studio 团队轻松排查并解决您可能遇到的任何问题。

启用更积极的优化

R8 包含一组额外的优化功能,这些功能在默认情况下处于停用状态。您可以通过在项目的 gradle.properties 文件中添加以下代码来启用这些额外的优化功能:

android.enableR8.fullMode=true

这些额外的优化功能会使 R8 的行为与 ProGuard 不同,因此可能会需要您添加额外的 ProGuard 规则,以避免运行时问题。例如,假设您的代码通过 Java Reflection API 引用一个类。默认情况下,R8 会假设您打算在运行时检查和操纵该类的对象(即使您的代码实际上并不这样做),因此它会自动保留该类及其静态初始化程序。

不过,在使用“完整模式”时,R8 不会做出这种假设,如果 R8 断定您的代码从不在运行时使用该类,它会将该类从应用的最终 DEX 中移除。也就是说,如果您要保留该类及其静态初始化程序,则需要在规则文件中添加相应的保留规则。

混淆问题排查

  • 把如下代码
-printusage <output-dir>/usage.txt

添加到您的自定义规则文件中。当您在启用 R8 的情况下构建应用时,R8 会按照您指定的路径和文件名输出应用中移除的所有代码的报告

  • 把如下代码
-printseeds <output-dir>/seeds.txt

添加到您的自定义规则文件中。当您在启用 R8 的情况下构建应用时,R8 会按照您指定的路径和文件名输出根据项目的保留规则确定的入口点的报告

  • Gradle 还会在 /build/outputs/mapping/release/(ProGuard 输出文件所在的文件夹)中创建一个名为 resources.txt 的诊断文件。此文件包含一些详细信息,比如,哪些资源引用了其他资源,哪些资源在使用,哪些资源被移除。

还可参阅R8常见问题解答页面ProGuard 的问题排查指南

附上实践项目规则文件

涉及到公司第三方库,一律不贴

# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/huning/androidsdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# Add any project specific keep options here:

# 代码混淆压缩比,在0~7之间
-optimizationpasses 5
#google推荐算法
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
# 避免混淆Annotation、内部类、泛型、匿名类
-keepattributes *Annotation*,InnerClasses,Signature,EnclosingMethod
# 抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable

# dump.txt文件列出apk包内所有class的内部结构
-dump class_files.txt
# seeds.txt文件列出未混淆的类和成员
-printseeds seeds.txt
# unsage.txt文件列出从apk中删除的代码
-printusage unused.txt
# mapping.txt文件列出混淆前后的映射
-printmapping mapping.txt

#不需混淆的Android类
-keep public class * extends android.app.Fragment
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.preference.Preference
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View
-keep public class com.android.vending.licensing.ILicensingService
-keep class android.support.** {*;}

# 处理support包
-dontnote android.support.**
-dontwarn android.support.**

-dontwarn javax.annotation.**
-dontwarn javax.inject.**

#自定义view
-keep public class * extends android.view.View{
    *** get*();
    void set*(***);
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
}

-assumenosideeffects class android.util.Log {
    public static *** d(...);
    public static *** v(...);
    public static *** i(...);
    public static *** e(...);
    public static *** w(...);
}
#保持 Serializable 不被混淆
-keepnames class * implements java.io.Serializable

#保持 Serializable 不被混淆并且enum 类也不被混淆
-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

#webview
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
    public *;
}
-keepclassmembers class * extends android.webkit.webViewClient {
    public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
    public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.webViewClient {
    public void *(android.webkit.webView, jav.lang.String);
}


#保持第三方jar
-libraryjars libs/commons-codec-1.3.jar
-libraryjars libs/jcore-android-2.5.5.jar
-libraryjars libs/jpush-android-3.8.5.jar
-libraryjars libs/mcssdk-1.0.1.jar
-libraryjars libs/MiPush_SDK_Client_3_4_5.jar
-libraryjars libs/wechat-sdk-android-with-mta-1.3.4.jar
-libraryjars libs/xUtils-2.6.14.jar
-libraryjars libs/universal-image-loader-1.9.5.jar
-libraryjars libs/nineoldandroids-library-2.4.0.jar

#alipay sdk
-keep class com.alipay.android.app.IAlixPay{*;}
-keep class com.alipay.android.app.IAlixPay$Stub{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback$Stub{*;}
-keep class com.alipay.sdk.app.PayTask{ public *;}
-keep class com.alipay.sdk.app.AuthTask{ public *;}


##Glide
-dontwarn com.bumptech.glide.**
-keep class com.bumptech.glide.**{*;}
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.AppGlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
  **[] $VALUES;
  public *;
}
#对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆
-keepclassmembers class * {
 void *(**On*Event);
 void *(**On*Listener);
}

#BaseRecyclerViewAdapterHelper
-keep class com.chad.library.adapter.** {
*;
}
-keep public class * extends com.chad.library.adapter.base.BaseQuickAdapter
-keep public class * extends com.chad.library.adapter.base.BaseViewHolder
-keepclassmembers  class **$** extends com.chad.library.adapter.base.BaseViewHolder {
  <init>(...);
}

#butterknife
-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }
-keepclasseswithmembernames class * {
    @butterknife.* <fields>;
}
-keepclasseswithmembernames class * {
    @butterknife.* <methods>;
}


#chargeQrcode
-keep class com.kd.charge.**{*;}

#loadsir
-keep class com.kingja.loadsir.**{*;}



##---------------Begin: proguard configuration for Gson  ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature

# For using GSON @Expose annotation
-keepattributes *Annotation*

# Gson specific classes
-dontwarn sun.misc.**
#-keep class com.google.gson.stream.** { *; }

# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { <fields>; }

# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
-keep class * extends com.google.gson.TypeAdapter
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer

# Prevent R8 from leaving Data object members always null
-keepclassmembers,allowobfuscation class * {
  @com.google.gson.annotations.SerializedName <fields>;
}

##---------------End: proguard configuration for Gson  ----------

##fastJson
-keep class com.alibaba.fastjson.** { *; }

#OkHttp3

-keep class okhttp3.internal.**{*;}
-dontwarn okhttp3.**
-dontwarn okio.**
-dontwarn javax.annotation.**

#Retrofit
-dontwarn okio.**
-dontwarn javax.annotation.**
-dontwarn sun.misc.**
-dontwarn sorg.codehaus.mojo.animal_sniffer.**
-dontwarn org.codehaus.**
-dontwarn java.nio.**
-dontwarn java.lang.invoke.**

#RxJava RxAndroid
-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.ArrayQueueField* {
long producerIndex;
long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
rx.internal.util.atomic.LinkedQueueNode producerNode;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
rx.internal.util.atomic.LinkedQueueNode consumerNode;
}


参考文章:

Android安全攻防战,反编译与混淆技术完全解析(下)

缩减、混淆处理和优化应用

上一篇:Tensorflow:tf.contrib.rnn.DropoutWrapper函数(谷歌已经为Dropout申请了专利!)、MultiRNNCell函数的解读与理解


下一篇:Keep三面:如何用Spring Security实现前后端分离?