前言
这阵子自己的心又长草了,静不下来~又挺迷茫的!在这个纷纷跳槽的季节,感觉还是应该让自己静下心来多学习学习。还是写写博客,总结总结~梳理下心里的野草。
上个月跟朋友讨论了这么一问题:“项目上线之后由于代码被混淆的缘故,导致收集到的异常信息看着很困难”,刚好最近在复习混淆方面的知识,在总结混淆的知识点的同时,顺便探讨总结下这问题。项目上线肯定避免不了的是对项目进行混淆、打包、签名和发布,可能还有APK加固等等,其实这流程并不复杂,都有一套明确的流程,所以整起来也不是很困难。而上面提到的“混淆导致上线后的异常信息查看起来挺困难”这问题,这几天也大概探讨完,打算记录在下篇文章~~那么这篇文章先开始扯淡吧!
Ⅰ.简述
混淆的概念:将Android项目进行打包之时,可以将项目里的包名、类名、变量名进行更改,使得代码不容易泄露,类似于对其apk中的文件加密.
混淆的作用:
- 1.增加Apk反编译之后代码泄露的困难性
- 2.生成的apk体积会缩小
什么是混淆?
Android SDK 本身就提供混淆的功能,将混淆开关进行开启后,开发者需要做的是对Android Studio工程项目中的proguard-rules.pro文件进行混淆白名单的配置.
那么什么是混淆白名单呢?其实就是指定一些包名、类名、变量等不可以被混淆。假设没指定白名单就进行混淆打包,而某某类的类名被混淆了(假设变成了a),那么可能其他引用或使用该类的类就找不到该类,说不定应用就会因此崩溃或是导致相应的功能无法使用.
那么所谓的混淆也就是配置混淆白名单,那么下面看看混淆之后的apk的内部结构.可以看到红圈圈出来的部分都是进行混淆的,而有部分是没有进行混淆的,比如黑圈圈出来的属性动画兼容库nineoldandroids,其包名类名就没有变成abc这样的代替符
上面我是用apk逆向助手对apk进行反编译,市场上的反编译工具有很多种,可以自行Google搜索。
补充
本篇文章记录的混淆知识点主要基于Android Studio开发工具。
Ⅱ.开始混淆
1.开启混淆开关
混淆的开关在项目/app/build.gradle文件里,看下面的截图,将minifyEnabled设置为true就是开启混淆,关于下面的配置代码可以直接写在build.gradle文件的android节点下
代码混淆一般是在上线前的apk打包才会去配置混淆开启,要是忘记配置的代码,那怎么办呢?直接进去Project Structrue,然后根据下面截图所标识的进行设置,如此这般,只要打release包就是开启混淆进行打包的.
2.设置混淆白名单
基于Android Studio创建的项目里有一文件名称为”proguard-rules.pro”的文件,路径是”项目/app/proguard-rules.pro”,没经过编辑之前,里面只有一些注释的代码,如下图
那么设置的混淆白名单又该怎么写呢?Google搜索的话会有很多博客上的模板可以复制进行套用.如下图,那么就可以进行参考,下面第三部分将常用的混淆指令和对应的注释都列举出来,基本常用的都有,有疏漏的那就自行搜索下.
Ⅲ.实际混淆指令
在应用中,大多数的混淆指令是已经确定的了,比如下面的基本指令部分,基本不用修改的。而其他的混淆指令,比如第三方的SDK/框架的混淆指令一般在其官方文档都可以找到,所以相对来说还是比较方便的,下面将这几天归类的混淆指令总结下.
基本指令:
# 设置混淆的压缩比率 0 ~ 7
-optimizationpasses 5
# 混淆后类名都为小写 Aa aA
-dontusemixedcaseclassnames
# 指定不去忽略非公共库的类
-dontskipnonpubliclibraryclasses
#不做预校验的操作
-dontpreverify
# 混淆时不记录日志
-verbose
# 混淆采用的算法.
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
#保留代码行号,方便异常信息的追踪
-keepattributes SourceFile,LineNumberTable
#dump文件列出apk包内所有class的内部结构
-dump class_files.txt
#seeds.txt文件列出未混淆的类和成员
-printseeds seeds.txt
#usage.txt文件列出从apk中删除的代码
-printusage unused.txt
#mapping文件列出混淆前后的映射
-printmapping mapping.txt
避免混淆Android基本组件,下面是兼容性比较高的规则:
-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.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
#不提示V4包下错误警告
-dontwarn android.support.v4.**
#保持下面的V4兼容包的类不被混淆
-keep class android.support.v4.**{*;}
避免混淆所有native的方法,涉及到C、C++
-keepclasseswithmembernames class * {
native <methods>;
}
避免混淆自定义控件类的get/set方法和构造函数
-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);
}
避免混淆枚举类
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
避免混淆序列化类
#不混淆Parcelable和它的实现子类,还有Creator成员变量
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
#不混淆Serializable和它的实现子类、其成员变量
-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();
}
避免混淆JSON类的构造函数
#使用GSON、fastjson等框架时,所写的JSON对象类不混淆,否则无法将JSON解析成对应的对象
-keepclassmembers class * {
public <init>(org.json.JSONObject);
}
避免混淆第三方SDK
# ==================环信混淆start=================
-keep class com.hyphenate.** {*;}
-dontwarn com.hyphenate.**
# ==================环信end======================
# ==================bugly start==================
-dontwarn com.tencent.bugly.**
-keep public interface com.tencent.**
-keep public class com.tencent.** {*;}
-keep public class com.tencent.bugly.**{*;}
# ==================bugly end====================
# ===============百度定位 start====================
-keep class vi.com.gdi.** { *; }
-keep public class com.baidu.** {*;}
-keep public class com.mobclick.** {*;}
-dontwarn com.baidu.mapapi.utils.*
-dontwarn com.baidu.platform.comapi.b.*
-dontwarn com.baidu.platform.comapi.map.*
# ===============百度定位 end======================
//备注:其他的第三方包的混淆指令可以到其官方文档去拷贝
避免混淆第三方框架
# ==================picasso框架 start===============
-keep class com.parse.*{ *; }
-dontwarn com.parse.**
-dontwarn com.squareup.picasso.**
-keepclasseswithmembernames class * {
native <methods>;
}
# ==================picasso end====================
# ==================EventBus start=================
-keep class org.greenrobot.** {*;}
-keep class de.greenrobot.** {*;}
-keepclassmembers class ** {
public void onEvent*(**);
void onEvent*(**);
}
# ==================EventBus end===================
# ==================okhttp start===================
-dontwarn com.squareup.okhttp.**
-keep class com.squareup.okhttp.** { *;}
-dontwarn okio.**
-keep class okio.**{*;}
-keep interface okio.**{*;}
# ==================okhttp end=====================
//备注:其它框架的混淆指令可以到其官方文档去拷贝
其它混淆指令
#避免混淆属性动画兼容库
-dontwarn com.nineoldandroids.*
-keep class com.nineoldandroids.** { *;}
#不混淆泛型
-keepattributes Signature
#避免混淆注解类
-dontwarn android.annotation
-keepattributes *Annotation*
#避免混淆内部类
-keepattributes InnerClasses
#避免混淆实体类,修改成你对应的包名
-keep class com.wyk.test.bean.** { *; }
-keep class com.wyk.test.event.** { *; }
-keep public class com.wyk.test.utils.eventbus.** { *;}
#避免混淆Rxjava/RxAndroid
-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
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;
}
#避免混淆js相关的接口
-keepattributes *JavascriptInterface*
-keep class com.wyk.test.js.** { *; }
Ⅳ.混淆配置注意点
1.假设当配置 “-libraryjars libs/jpush-android-2.1.6.jar” 对jar包进行混淆白名单化,如果gradle报错的话,可以考虑注释掉(格式:-libraryjars [jar包名])这样的配置信息.采用下面的配置信息进行替换
-dontwarn cn.jpush.**
-keep class cn.jpush.** { *; }
2.下面是对属性动画兼容库的混淆白名单配置信息,刚开始觉得只是保持com.nineoldandroids包下的类不被混淆,后来经过反编译混淆后的apk包,发现效果是”不混淆该class com.nineoldandroids包下的类、子包和子包的类,也不混淆其中类的成员变量.
-keep class com.nineoldandroids.** { *;}
Ⅴ.其它
1.混淆套用模板
个人觉得下面链接的博文就写得非常好,所以可以进行参考.
参考博文:5分钟搞定android混淆
2.资源混淆
Proguard混淆只是针对代码进行混淆,解压之后的apk包还是能看到项目的资源文件和其名称,比如布局、logo图片等等.这时可以选择对资源文件进行混淆,下面两个链接是腾讯推出的资源混淆工具相关的博文,可以参考.
资源混淆工具相关的博文
http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=42
https://github.com/shwenzhang/AndResGuard/blob/master/README.zh-cn.md
3.加固
为了使得apk更加不容易被破解,混淆之后还可以对apk进行加固,现今市面上的加固技术有很多种,有360加固、爱加密加固、梆梆加固等等,可以自行选择,加固技术就相当于给apk包加多一个壳,相应的体积也会增大,大概增大1M~2M左右,需要的话可以自行搜索,加固还是挺简单的.
4.参考链接
http://blog.csdn.net/maxwell_nc/article/details/51998766
http://blog.csdn.net/chen930724/article/details/49687067
http://blog.csdn.net/lovexjyong/article/details/24652085
http://www.jianshu.com/p/f3455ecaa56e
Ⅱ.下面讲异常收集(可忽略)
这篇文章主要的技术点是异常收集,项目上线前除了混淆、打包、加固、签名和发布等,还有一项是无可避免的,就是对线上的应用进行各种统计,对应用进行各种统计包括异常的收集统计、应用的渠道下载率、活跃量、留存率和页面访问路径统计等等,有很多第三方的统计SDK可供接入使用,比如友盟+、百度、诸葛IO 等第三方,精细点的话可以考虑下无埋点技术等. 只要在上线前集成了统计SDK,那么就可以在其相应的后台看到上线后各种数据的统计报表.
统计对于运营和项目维护还是很有必要的,开发人员可以看到收集到的异常,然后对异常进行分析并找到相应解决的方案,那么这里就要开始文章的主题了,下面的截图是友盟+后台收集到的异常信息,在其异常信息下面还可以收集到该异常发生的手机型号、版本和渠道。看下异常信息吧,当看到红圈圈出来的部分,是不是就迷惑了,异常信息里怎么会有abc 这样的替代符,本来异常信息可以让开发者清楚知道异常发生的地方,可以使其轻易定位到,但现在呢?难不成靠猜的方式去定位异常,那就呵呵了.怪不得朋友说异常信息定位问题非常迷惑,那么下面开始来整整这迷惑.
Ⅲ.还原混淆信息的方式
针对上面的异常信息出现abc 的替代符,主要是由于混淆打包导致的,上面abc 其实是项目的类名或变量名的代替符,那么如果apk没有经过混淆就会导致apk源码泄露或被二次打包,虽说混淆了之后的apk还是很大风险会泄露,但相对来说代码泄露的难度是增大了,所以混淆是不可缺的。那么上面的异常信息又该如何定位Bug呢?
Android SDK工具包就提供了解决的工具,sdk\tools\proguard\bin路径下名为”proguardgui.bat”和”retrace.bat”(windows和linux下,工具的后缀名不同)的两个工具,前者是通过图形化的方式去将被混淆的异常信息反编译,后者则通过命令行的方式将被混淆的异常信息反编译.那么在使用这工具前,还得有一个叫”mapping.txt”的文件,看下面截图,这是在打包apk完成后生成的一个文件,主要记录着混淆前后的信息映射关系。
每次打包apk 完成后生成的mapping.txt 都是有必要保存的,那怎么使用上面提到的 “proguardgui.bat”和”retrace.bat” 这两个工具呢?看下面截图即可,简简单单搞定proguardgui.bat ,但抱歉的是本人试了很多次,都不能将异常信息转换回去,而最无语的是搜索到的文章介绍的方法跟我操作的完全一样,这时候我就发现了经常碰到的奇葩问题,基本文章上演示截图的异常信息都是一样的,尼玛这些文章的截图既然都是抄袭的,难不成Android SDK提供的”proguardgui.bat” 图形化工具已经失效了,接着试试”retrace.bat” 工具.上面也提到了 “proguardgui.bat”和”retrace.bat” 这两个工具基本是一样的,只是使用方式不同,一个是图形化方式,一个则是命令行方式。
在retrace.bat命令行工具里反编译异常,使用的指令为
格式:retrace.bat|retrace.sh [-verbose] mapping.txt [<stacktrace_file>]
例如:retrace.bat -verbose d:/mapping.txt d:/wyk_stacktrace.txt
那么接着就验证下命令行形式的 “retrace.bat”,并不同于上面的proguardgui.bat 工具有面板可以粘贴报错信息,所以先把异常信息保存为txt文件,然后命令行进入Android SDK存放的路径sdk\tools\proguard\bin目录,根据上面的指令格式进行输入,结果如下:
结论:”proguardgui.bat”和”retrace.bat” 这两个工具包无法还原Apk线上被混淆的异常信息.
上面的截图就是使用”retrace.bat” 工具的反编译异常信息的结果,可以看到abc 的标识符依然存在,所以仍得不到完整的异常信息。反复试了很多次,依旧无果,还是找找有没有其他方式吧~~话说在Android SDK的sdk\tools\proguard\lib目录下有”proguardgui.jar”和”retrace.jar” 这两个jar包,上面使用到的”proguardgui.bat”和”retrace.bat” 这两个工具可能是基于这两个jar包的,思考如果直接使用这两jar包尝试反编译异常信息的话是否有解。先试试retrace.jar 这个jar包,命令行进入到jar包所在的目录,在命令行输入如下指令,输出的信息和上面的retrace.bat工具输出的一样,依然没有完整的异常信息.
格式:java -jar retrace.jar [-verbose] mapping.txt [<stacktrace_file>]
例如:java -jar retrace.jar -verbose d:/mapping.txt d:/error.txt
接着试试proguardgui.jar,命令行进入jar包所在的目录,输入 “java -jar proguardgui.jar” 启动proguard工具,看到的界面和proguardgui.bat 是一样的,应该说proguardgui.bat 启动的也是这个jar包,验证之后还是没有得到完整的异常信息.
结论:”proguardgui.jar”和”retrace.jar” 这两个jar包无法还原Apk线上被混淆的异常信息.
Ⅳ.根据mapping.txt文件验证
上面的工具可以还原被混淆的异常信息,其原理是因为mapping.txt存在其混淆前后的映射信息,那是不是可以根据被混淆的一小段异常信息在mapping.txt文件查找相应的映射关系,拷贝被混淆的异常信息在mapping.txt文件进行全文搜索,下面图1是收集在统计异常后台的信息,图2是在mapping.txt文件查找图1红圈部分的映射信息.图3也是异常信息和映射关系.
结论:通过上面异常信息混淆前后的映射关系,切记打包时将相应的apk和生成的mapping.txt进行对应保存,这将对上线之后的Bug追踪和维护起着非常重要的作用;
Ⅴ.其他还原混淆异常信息的方式
上面根据mapping.txt查找信息映射关系的方式,显然不适合线上Bug的追踪和应用的维护,因此就得另找出口,经常使用到的统计异常SDK有友盟+和Bugly,早前一直都在用友盟+的统计异常SDK,之后由于统计数据不及时和疏漏,所以之后的应用选择接入Bugly,Bugly针对异常的收集还是非常及时和准确的。
这些统计异常的SDK其实都有提供还原被混淆异常信息的功能,这样对开发者就非常友好了,该功能的位置在SDK后台的异常信息上边,只需要导入异常信息对应的应用版本的mapping文件,点击”解析”按钮就可以看到原始的异常信息。
在友盟+的统计后台亲测发现,被混淆的异常无法还原,探究了几番仍找不到原因.而在Bugly的统计后台亲测是有效的,可以看到下面被混淆的异常信息和还原之后的异常信息.
Ⅵ.总结
- App线上异常的追踪,可以选择友盟+、百度、诸葛IO等第三方,精细点的话可以考虑下无埋点技术等;
- 经过测试发现,Android SDK包的”proguardgui.bat” 和”retrace.bat” 这两个工具包无法还原Apk线上被混淆的异常信息;
- Apk打包后生成的的mapping文件保存着代码混淆前后的映射关系;
- 第三方SDK的统计后台一般都提供还原 “被混淆异常信息” 的功能;
- 切记 打包时将apk版本和生成的mapping.txt进行对应保存.
我的模版
#############################################
#
# 对于一些基本指令的添加
#
#############################################
# 代码混淆压缩比,在0~7之间,默认为5,一般不做修改
-optimizationpasses 5
# 混合时不使用大小写混合,混合后的类名为小写
-dontusemixedcaseclassnames
# 指定不去忽略非公共库的类
-dontskipnonpubliclibraryclasses
# 这句话能够使我们的项目混淆后产生映射文件
# 包含有类名->混淆后类名的映射关系
-verbose
# 指定不去忽略非公共库的类成员
-dontskipnonpubliclibraryclassmembers
# 不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify,去掉这一步能够加快混淆速度。
-dontpreverify
# 保留Annotation不混淆
-keepattributes *Annotation*,InnerClasses
# 避免混淆泛型
-keepattributes Signature
# 抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable
# 指定混淆是采用的算法,后面的参数是一个过滤器
# 这个过滤器是谷歌推荐的算法,一般不做更改
-optimizations !code/simplification/cast,!field/*,!class/merging/*
#############################################
#
# Android开发中一些需要保留的公共部分
#
#############################################
# 保留我们使用的四大组件,自定义的Application等等这些类不被混淆
# 因为这些子类都有可能被外部调用
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Appliction
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.preference.Preference
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.view.View
-keep public class * extends android.support.v4.**
-keep public class * extends android.support.v7.**
-keep public class * extends android.support.annotation.**
-keep public class com.android.vending.licensing.ILicensingService
-dontnote android.net.http.*
-dontnote org.apache.commons.codec.**
-dontnote org.apache.http.**
-dontwarn android.support.design.**
-keep class android.support.design.** { *; }
-keep interface android.support.design.* { *; }
-keep public class android.support.design.R$ { *; }
# 保留support下的所有类及其内部类
-keep class android.support.** {*;}
# 保留R下面的资源
-keep class **.R$* {*;}
# 保留本地native方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
}
# 保留在Activity中的方法参数是view的方法,
# 这样以来我们在layout中写的onClick就不会被影响
-keepclassmembers class * extends android.app.Activity{
public void *(android.view.View);
}
# 保留枚举类不被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# 保留我们自定义控件(继承自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);
}
# 保留Parcelable序列化类不被混淆
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
# 保留Serializable序列化的类不被混淆
-keepnames class * implements java.io.Serializable
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <fields>;
!private <fields>;
!private <methods>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# webView处理,项目中没有使用到webView忽略即可
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
public *;
}
-keepclassmembers class * extends android.webkit.webViewClient {
public *;
}
-dontwarn com.zhixun.extraterrestrial.util.ForbidCopyPasteUtil
#############################################
#
# 项目中特殊处理部分
#
#############################################
#-----------处理实体类---------------
# 在开发的时候我们可以将所有的实体类放在一个包内,这样我们写一次混淆就行了。
#-keep class com.blankj.data.bean.**{ *; }
# 或者对实体类都加 @Keep 即可
# @Keep
-keep,allowobfuscation @interface android.support.annotation.Keep
-keep @android.support.annotation.Keep class *
-keepclassmembers class * {
@android.support.annotation.Keep *;
}
#-----------处理第三方依赖库---------
#--Retrofit
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes Signature
-keepattributes Exceptions
#--RxJava RxAndroid
-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
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;
}
#--Gson
-keepattributes Signature
-keepattributes *Annotation*
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.stream.** { *; }
# Application classes that will be serialized/deserialized over Gson 下面替换成自己的实体类
#-keep class com.example.bean.** { *; }
#--Okhttp
-dontwarn com.squareup.okhttp3.**
-keep class com.squareup.okhttp3.** { *;}
-dontwarn okio.**
-dontwarn okhttp3.internal.platform.ConscryptPlatform
#--Glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
#--Utils
-keep class com.blankj.utilcode.** { *; }
-keepclassmembers class com.blankj.utilcode.** { *; }
-dontwarn com.blankj.utilcode.**
#--RxLifeCycle
-keep class com.trello.rxlifecycle2.** { *; }
#--rxbinding2
-keep class com.jakewharton.rxbinding2.view.ViewScrollChangeEventObservable { *; }
-keep class com.jakewharton.rxbinding2.view.RxViewKt { *; }
-keep class com.jakewharton.rxbinding2.view.ViewScrollChangeEventObservable$* { *; }
-keep class com.jakewharton.rxbinding2.view.ViewScrollChangeEventObservable$Listener { *; }
-keepattributes Exceptions,InnerClasses
#--dagger2
-keep class dagger.** { *; }
-dontwarn dagger.**
#--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>(...);
}
#--指纹解锁
-keep class com.fingerprints.service.** { *; }# MeiZuFingerprint
-keep class com.samsung.android.sdk.** { *; }# SmsungFingerprint
-dontwarn com.samsung.android.sdk.**
-dontwarn com.fingerprints.service.**
#--k线图
-keep class com.github.mikephil.charting.** { *; }
# Realm
-keep class io.realm.annotations.RealmModule
-keep @io.realm.annotations.RealmModule class *
-keep class io.realm.internal.Keep
-keep @io.realm.internal.Keep class * { *; }
-dontwarn javax.**
-dontwarn io.realm.**
#--换肤
-keep class skin.support.** { *; }
-dontwarn skin.support.**
#--拷贝
-dontwarn net.soureceforge.pinyin4j.**
-dontwarn demo.**
-libraryjars libs/pinyin4j-2.5.0.jar
-keep class net.sourceforge.pinyin4j.** { *;}
-keep class demo.** { *;}
-keep class com.hp.** { *;}