前言
某一个夜深人静的加班夜,也许你在做代码混淆……ProGuard 是一个比较枯燥且没有成就感的技术,但 Android 项目没有了 ProGuard 还真就不行。在 Android 中使用 ProGuard 可以起到压缩、优化、混淆、预检的作用,保证我们开发出的 APK 更健壮。
近期的一次发版中,同事反应一个发生在某米手机(没有黑~)上的奔溃 bug,手头的测试真机数量有限且均无此问题。后来使用云测试进行调试,上传了 release 和 debug 版的 apk,发现那款机型的确出现问题。经过分析,问题定位在了代码混淆问题上,因为想到自己之前的一次发版中也曾有过这个问题。
又一次遇到这样的问题,觉得有必要好好掌握一下正确地写代码混淆规则的姿势了,而不只是在使用第三方和轮子时,单纯地 copy 开源库的 ProGuard。
使用
编写 ProGuard 文件
受《App 研发录》启发,收集和整理了 Android 开发中国内项目常用库的混淆规则,按照包老师的思路,是一个三步走的过程。
- 基本混淆
- 针对 App 的量身定制
- 针对第三方 jar 包的解决方案
整理了一个类似模板的项目吧,希望对大家有用,轻松搞定混淆。并已开源到 GitHub——Android-proguard-cn-collection,收集整理中,如果你有补充,期待你的 PR。
基本混淆
最基本的配置信息,任何 App 都要使用,基本不用动。(代码及注释来自《App 研发录》)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
| # # 1. 基本混淆====================================================================================== # ## 1.1 基本指令------------------------------------------------------------- # 代码混淆压缩比,在 0~7 之间,默认为 5,一般不需要改 -optimizationpasses 5
# 混淆时不使用大小写混合,混淆后的类名为小写 -dontusemixedcaseclassnames
# 指定不去忽略非公共的库的类 -dontskipnonpubliclibraryclasses
# 指定不去忽略非公共的库的类的成员 -dontskipnonpubliclibraryclassmembers
# 不做预校验,preverify 是 proguard 的 4 个步骤之一 # Android 不需要 preverify,去掉这一步可加快混淆速度 -dontpreverify
# 有了 verbose 这句话,混淆后就会生成映射文件 # 包含有类名 -> 混淆后类名的映射关系 # 然后使用 printmapping 指定映射文件的名称 -verbose -printmapping proguardMapping.txt
# 指定混淆时采用的算法,后面的参数是一个过滤器 # 这个过滤器是谷歌推荐的算法,一般不改变 -optimizations !code/simplification/arithmetic,!field
|
针对 App 的量身定制
这部分做部分修改,保留你的项目中的实体类、自定义 View、内嵌类、WebView、反射等不被混淆,如果没有使用到的,忽略即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| # # 2. 针对 App 的量身定制============================================================================ # ## 2.1 保留实体类和成员不被混淆----------------------------------------------- # 例如(替换成你的包名结构): -keep class com.yourpackage.app.model.entity.** { public void set*(***); public *** get*(); public *** is*(); } #---------------------------------------------------------------------------
# ## 2.2 内嵌类--------------------------------------------------------------
#---------------------------------------------------------------------------
# ## 2.3 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); } #---------------------------------------------------------------------------
# ## 2.4 对 JavaScript 的处理------------------------------------------------ # 保留 JS 方法不被混淆 #-keepclassmembers class com.example.xxx.MainActivity$JSInterface1 { # <methods>; #} #---------------------------------------------------------------------------
# ## 2.5 处理反射------------------------------------------------------------
#---------------------------------------------------------------------------
# ## 2.6 对于自定义 View 的解决方案-------------------------------------------- -keep class com.yourpackage.a.** { *; } #--------------------------------------------------------------------------- #===================================================================================================
|
针对第三方 jar 包的解决方案
项目中使用到很多第三方提供的 SDK,如下列出了针对 android-support-v4.jar 和第三方 jar 包的解决方案。根据使用到的第三方库,查找对应的 ProGuard,加入到混淆规则中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| # # 3. 针对第三方 jar 包的解决方案===================================================================== # ## 3.1 针对 android-support-v4.jar 的解决方案------------------------------- # 如果是直接包含的 jar 包的话,这样写 # 如果 gradle 报错的话,可以考虑注释掉 -libraryjars #-libraryjars libs/android-support-v4.jar -dontwarn android.support.v4.** -keep class android.support.v4.** { *; } -keep interface android.support.v4.app.** { *; } -keep public class * extends android.support.v4.** -keep public class * extends android.app.Fragment #---------------------------------------------------------------------------
# ## 3.2 其他的第三方 jar 包的解决方案-----------------------------------------
#--------------------------------------------------------------------------- #===================================================================================================
|
在项目中指定混淆文件
将 Module 下的 build.gradle 中 minifyEnabled 设置为 true。
1 2 3 4 5 6 7 8
| android { buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } }
|
以上三部分混淆规则,可以写在一个 ProGuard 文件中,即创建 Android 工程后默认生成的 proguard-rules.pro,proguardFiles 指定使用'proguard-rules.pro'(默认就是这样)。
或者,可以写多个 ProGuard 文件,分别指定,这种方式更直观。
1 2 3 4
| proguardFile 'proguard-a.pro' proguardFile 'proguard-b.pro' proguardFile 'proguard-c.pro' ...
|
总结
- 如果需要代码混淆,请尽早做
- 测试工作要基于混淆包进行,才能尽早发现问题
- 发版前,要额外测试推送、分享、支付等功能
感谢
包建强老师的《App 研发录》