资讯动态

Android代码混淆实战:Gradle与R8配置及踩坑指南

发布时间:2026/9/12 3:15:04 来源:尧图企业网站定制
最近在帮忙 review 一个 Android 老项目的 release 构建配置第一眼看到minifyEnabled false我心里就咯噔一下。这种包发出去类名、方法名、资源名基本等于明文别人拿反编译工具一拖就能看到核心逻辑。我把开关改成true补齐proguard-rules.pro之后包体从 23.6 MB 掉到 17.1 MB而且之前那种“业务代码被扒走”的担心也基本断了根。这篇文章我打算把基于 Gradle 的代码混淆配置从头到尾拆一遍从概念到实战再到我踩过的一堆坑能说的全写下来。做移动开发的尤其是 Android 方向的朋友不管你现在是在维护老项目还是准备发新包这篇内容应该都能帮你省不少时间。1. 代码混淆到底帮我们解决了什么1.1 混淆不是“把名字变短”这么简单很多人以为代码混淆就是把HomeActivity变成a.a.a好让反编译的人看不懂。这个说法只讲对了一小半。Gradle 里的混淆动作其实是 ProGuard 或 R8 在帮你做四件事压缩Shrink掉没有用到的代码优化Optimize字节码混淆Obfuscate类名、方法名和字段名最后是预校验Preverify。R8 在 Android Gradle Plugin 3.4.0 之后成为默认的代码压缩和混淆工具它把优化和混淆合并到一起做了构建速度比以前的 ProGuard 快不少。真正落到工程上这四项能力的收益是很直观的。压缩能帮你删掉整个项目里根本不会走到的那部分代码比如某个 SDK 依赖只用了两个方法剩下的几百个类压根不会被引用那它们就有可能在构建时被移除。优化会做一些字节码层面的精简比如把一些冗余的指令合并掉。混淆则是把符号表改掉让反编译出来的结果很难读。最后预校验这步在现代 Android 构建里已经不太强调但流程上还保留着。所以你看混淆这件事既是安全手段也是性能手段。它最直接的价值是两个一是让别人扒代码的难度直线上升二是减少包体积、加快启动时的类加载效率。配合资源压缩一起用效果比单独开混淆要明显得多。1.2 R8 和 ProGuard到底选谁我用 Gradle 配置混淆的时候经常看到有人还在手动指定proguard-android.txt然后再加一个本地 ProGuard 版本这种做法其实已经过时了。只要你的 Android Gradle Plugin 版本在 3.4.0 以上R8 默认就参与 release 构建的压缩混淆流程。你不需要额外引入 ProGuard 的 jar 包也不需要单独区分 R8 规则和 ProGuard 规则因为 R8 完全兼容 ProGuard 的 keep 规则语法。实际项目中你写的-keep、-dontwarn、-keepattributes这些规则R8 都能识别。那 ProGuard 是不是就完全没用也不至于。老的库或者某些定制化的需求可能还依赖proguard-android.txt这种系统默认规则文件。但新项目我建议直接拥抱 R8别再折腾额外的 ProGuard 配置。R8 的问题也有比如它比 ProGuard 更激进某些写法在你本地没暴露问题在 release 包上反而会出幺蛾子。所以配置完成后一定要做一次完整的 release 回归测试这一点后面我会专门讲。1.3 不是所有项目都适合立刻全局开混淆在动手改配置之前我要先说句泼冷水的话混淆不是无脑开的。如果你的项目里大量使用反射、动态加载、JNI、SPI 这种机制或者依赖了某些不兼容混淆的第三方库那直接开启可能会导致一堆运行时崩溃。我见过最典型的一个项目成百上千个类靠反射去做路由分发混淆一开线上直接白屏最后花了一整天才排查出来是某个路由表的类被改名了。所以正确姿势是先评估自己项目的技术构成再决定开启力度。绝大多数常规 App 是适合开混淆的但规则文件要跟着项目实际情况来补齐。你可以先跑一次 release 构建看看报错和崩溃到底集中在哪再一条一条补规则。这比一上来就全盘 keep 要健康得多。2. 在 Gradle 里把第一套混淆配置跑起来2.1 最小可用配置长什么样这里我直接给出一份最小可用配置。假设你用的是主流的 Android Gradle PluginAGP 7.x 或 8.x打开app/build.gradle如果是 Kotlin DSL 就是app/build.gradle.kts在android节点下面找到buildTypesandroid { buildTypes { release { minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile(proguard-android-optimize.txt), proguard-rules.pro } } }这里minifyEnabled true是总开关打开之后就代表 release 构建会执行压缩和混淆。shrinkResources true是资源压缩开关它会配合代码压缩把没有被引用到的资源文件也一并移除。注意shrinkResources依赖minifyEnabled如果minifyEnabled是 false资源压缩不会生效。proguardFiles这行是规则文件的入口。getDefaultProguardFile(proguard-android-optimize.txt)指向 Android SDK 自带的默认规则里面已经包含了一些对系统组件、注解、枚举等场景的基础保护。后面的proguard-rules.pro是你自己的规则文件项目创建时通常会自动生成在app/目录下。两个文件是叠加关系R8 会同时加载它们。我建议你把这个配置放进 release 里就够了。debug 构建保持默认不混淆这样开发调试的时候堆栈信息还是原始类名加日志也方便。有些团队喜欢给 debug 也开混淆来提前暴露问题我不太推荐因为会拖慢每次 builds 的速度而且调试体验会差很多。2.2 proguardFiles 的加载顺序和文件拆分proguardFiles支持传入多个文件我见过有项目把规则文件拆成proguard-rules-base.pro、proguard-rules-thirdparty.pro、proguard-rules-datamodel.pro这种结构到buildTypes里再一起加进去release { minifyEnabled true shrinkResources true proguardFiles( getDefaultProguardFile(proguard-android-optimize.txt), proguard-rules-base.pro, proguard-rules-thirdparty.pro, proguard-rules-datamodel.pro ) }规则文件拆分的好处是职责清晰第三方库相关的 keep 规则集中维护后续升级 SDK 的时候只需检查对应文件。坏处是文件多了之后你可能忘了某个规则写在哪所以我个人建议中小型项目一个proguard-rules.pro就够了顶多拆成“通用规则”和“第三方规则”两个文件。文件内部要善用注释每一条 keep 规则下面写清楚为什么保留这样三个月后再来看自己也能秒懂。2.3 先把 Gradle 环境折腾明白了再说混淆在配置混淆之前很多人的 Gradle 构建其实都卡在环境问题上。特别是国内开发者每次新建或导入一个 Android 项目Gradle 下载慢或者干脆失败是家常便饭。这里我先补充几个和 Gradle 本身相关的实操点因为构建都跑不起来的话聊混淆规则毫无意义。Gradle 版本是通过gradle/wrapper/gradle-wrapper.properties里这一行控制的distributionUrlhttps\://services.gradle.org/distributions/gradle-8.7-bin.zip默认指向 Gradle 官方服务器国内下载经常只有几十 KB/s。我一般会改成腾讯或阿里的镜像地址实测能快很多。比如腾讯镜像distributionUrlhttps\://mirrors.cloud.tencent.com/gradle/gradle-8.7-bin.zip如果你公司网络连镜像也走不动那就用离线包方案手动下载对应版本的 zip 包放到用户目录~/.gradle/wrapper/dists/下对应的 Gradle 版本目录里重新执行构建时 Gradle 检测到本地已有 zip就不会再去下载了。除了 Gradle 本身依赖库的下载也要配镜像。项目根目录settings.gradle或build.gradle里的repositories可以加上国内镜像源pluginManagement { repositories { maven { url https://mirrors.cloud.tencent.com/nexus/repository/maven-public/ } google() mavenCentral() gradlePluginPortal() } } dependencyResolutionManagement { repositories { maven { url https://mirrors.cloud.tencent.com/nexus/repository/maven-public/ } google() mavenCentral() } }这样处理之后Gradle 构建速度和下载失败率都会明显好转。等你把环境理顺再回头配混淆心态会稳很多。2.4 配置完怎么验证有没有真正生效配置写完之后别急着说“完了”先验证一下。命令行到项目根目录执行./gradlew assembleReleaseLinux 或 macOS 下如果提示没有执行权限先执行chmod x gradlew。Windows 下用gradlew.bat assembleRelease。构建完成后去app/build/outputs/apk/release/目录拿 APK。要验证混淆是否真的生效最直接的方式是用反编译工具打开 APK比如 jadx。把 APK 拖进去看源码里的类名和方法名。如果都是a.a.a这种短名说明混淆已经工作。如果你还能看到完整的HomeActivity、UserManager这类可读性极强的名称那说明要么规则里把它 keep 住了要么整个混淆都没生效回头查开关和规则文件路径。另一个重要产物是 mapping 文件路径在app/build/outputs/mapping/release/mapping.txt。这份文件记录了混淆前后的类名、方法名、字段名映射关系线上崩溃日志要靠它还原。我强烈建议每次发版都把这个文件备份到版本管理工具或对象存储里不然线上出问题你只能干瞪眼。3. 混淆规则怎么写才不踩雷3.1 keep 规则四件套先吃透再动手R8 的规则语法延续了 ProGuard 的写法核心其实是四组关键字-keep、-keepclassmembers、-keepclasseswithmembers、-dontwarn。-keep是作用最猛的指定类和类成员都保留既不会被移除也不会被混淆名字。-keepclassmembers只保留指定的成员类本身的名字不保证。-keepclasseswithmembers是当类和指定成员同时存在时才整体保留。-dontwarn用来忽略某些警告信息一般配合缺失依赖的情况使用。通配符方面*匹配任意类名、方法名或字段名但不包括包名分隔符。**可以匹配任意包名和类名包括带包名的完整形式。举个例子-keep class com.example.** { *; }这一条表示com.example包下所有类以及这些类的所有成员全部保留。这是常见但也是最容易滥用的一条规则因为它会关闭 R8 对com.example包下代码的压缩能力。我见过有人图省事直接写-keep class ** { *; }这种规则千万不要出现在正式项目里它等于把整个混淆功能废掉一半包体积一点都不会缩小。-keepattributes则用来保留类的某些属性信息比如-keepattributes Signature -keepattributes *Annotation* -keepattributes InnerClasses, EnclosingMethod -keepattributes Exceptions, Deprecated, SourceFile, LineNumberTableSignature对泛型解析很重要尤其在使用 Gson、Retrofit 这类序列化库时如果缺了它泛型类型信息会丢失反序列化出来的对象可能缺字段。*Annotation*保留注解比如SerializedName、Keep这类注解要生效就得保留。SourceFile, LineNumberTable保留源码文件和行号线上捕获到的崩溃堆栈可以对应到原始代码行这对排查问题帮助极大。3.2 Android 系统组件的保活清单Android 的AndroidManifest.xml里注册的组件比如 Activity、Service、BroadcastReceiver、ContentProvider这些类名会被系统通过反射的方式加载。如果它们被混淆成a.b.c系统按 manifest 里的字符串找不到对应类启动时直接抛ClassNotFoundException。所以几乎每个项目都需要这么一段规则-keep public class * extends android.app.Activity -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.Application这段规则的核心思想不是“我要保留所有页面”而是“所有 manifest 里可能被系统反射加载的组件类名必须保持原样”。同理如果你自定义了View并且在布局 XML 里用全类名引用它那么 View 的类名也要保留-keep public class * extends android.view.View如果你用了ViewBinding或者DataBinding绑定的类是通过生成代码来引用的一般默认规则会覆盖但保险起见我也会在规则里加一行保留*Binding、*Impl结尾的类防止生成类名被改。3.3 反射、注解、枚举、序列化这些场景怎么处理反射是混淆的头号天敌。源代码里你写Class.forName(com.example.router.RouterTable)编译器并不知道这个字符串和哪个类绑定R8 也不会主动保护它。如果RouterTable被混淆走了运行时肯定找不到。这种场景没有通用解只能靠 keep 规则把被反射的类、方法或字段保护起来-keep class com.example.router.RouterTable { *; } -keepclassmembers class com.example.router.** { *; }如果反射调用的成员比较多可以只 keep 类的成员而不 keep 类名-keepclassmembers class com.example.reflect.** { methods; fields; }注解相关的场景也经常被忽略。比如你自己定义了一个PageRoute注解并用它来标记页面路由关系那么注解类本身要保留被注解标记的类也可能需要保留。规则写法是-keep interface com.example.annotation.PageRoute -keep com.example.annotation.PageRoute class * { *; }枚举类是另一个重灾区。枚举的values()和valueOf(String)可能会被系统或第三方库反射调用通常加这么一段-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); }序列化场景比如实现了Serializable接口的类R8 可能移除serialVersionUID字段导致反序列化时版本不匹配。建议加规则-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(); }3.4 第三方库规则的典型案例第三方库是否需要手写 keep 规则取决于这个库是否在自己的consumer-rules.pro里带了混淆规则。现代 Android 库一般都会在build.gradle里配置consumerProguardFiles依赖它的时候规则会自动合并进你的构建流程。比如 Retrofit、OkHttp、Glide 这些主流库它们自身已经带了很完整的 consumer 规则你不需要额外操心。但有些库不带或者带得不全。Gson 就是典型例子。Gson 通过反射读取类的字段名来匹配 JSON key如果你的数据模型类被混淆成a、bJSON 里对应的username字段就找不到了解析结果直接是 null。解决思路有几个第一种直接用Keep注解标注数据类。AndroidX 提供了androidx.annotation.Keep在类上加上这个注解R8 会保留它import androidx.annotation.Keep; Keep public class UserInfo { private String name; private int age; }第二种在 proguard 规则里匹配SerializedName注解。因为只要字段上有这个注解Gson 存的是注解里的字符串值类名混淆无所谓但字段名必须保留否则反射找不到字段-keepclassmembers class * { com.google.gson.annotations.SerializedName fields; }第三种最粗暴但最安心直接 keep 整个数据模型包-keep class com.example.data.model.** { *; }代价是这些类无法被压缩包体积会大一点。我个人的建议是数据模型类字段不多且相对稳定用Keep或SerializedName规则都行如果你用的是 Kotlin配合kotlinx.serialization则不需要 Gson 那套规则因为它是编译期生成序列化器的和反射无关。Retrofit 的情况稍微复杂一点。它内部用动态代理和泛型擦除来做接口转换如果你的接口定义返回类型是CallListUserInfoR8 在优化时可能把泛型 Signature 弄丢导致运行时类型解析失败。所以 Retrofit 场景下-keepattributes Signature是必须的。大多数时候 Retrofit 的 consumer 规则里已经写了但如果你在规则文件里看到大量-dontwarn retrofit2.**之类的提示别慌那是库作者为了避免混淆时产生警告而预设的。3.5 注解处理器和生成代码的兼容处理项目里用 Room、Dagger、Hilt、ButterKnife 这类注解处理器时它们会生成一堆_Impl结尾的类。这些生成类是在编译期被引用的R8 在压缩时能看到引用关系通常不会误删。但偶尔也会出现“生成类的方法被反射调用”的情况导致运行时报错。保险的做法是对生成的代码包加 keep-keep class **.*Impl { *; }不过这条规则范围有点广我一般只在明确遇到问题时才加。同理如果你的项目还停留在老旧的ButterKnife时代记得保留R2字段和ButterKnife注解对应的绑定类。4. 混淆后崩溃排查实录五个经典案例4.1 release 包启动闪退堆栈全是 a.a.a真机运行 release 包一打开就闪退。抓日志发现ClassNotFoundException而且堆栈里都是a.a.a这种短类名。这基本是某个通过反射加载的类被混淆了。我当时排查的路径是先看崩溃发生在哪个模块然后确认这个模块里有没有用Class.forName或context.getClassLoader().loadClass。定位到具体类之后在proguard-rules.pro里加对应的 keep 规则。如果反射点很多可以考虑把整个路由相关的包 keep 住。这里有个经验不要把 keep 规则写得过于局部像-keep class com.example.router.RouterTable { methods; }这种看起来精准但如果你今天漏了明天又加维护成本很高。对于路由、插件化这类靠反射的模块-keep class com.example.router.** { *; }反而是更省心的选择。4.2 接口返回的 list 全是 null但直接运行 debug 没问题开发同学反馈线上包请求接口后列表数据全是空。用 debug 包跑一切正常release 包就不行。这类问题一抓一个准基本都是数据模型类被混淆字段名对不上 JSON key 了。用 Gson 的话解决方案就是前面说的SerializedName规则或者直接在模型类上打Keep。如果你用的是 Retrofit 加 Gson 组合还要检查一下-keepattributes Signature是否保留。如果缺了 Signature泛型ListUserInfo的UserInfo类型信息会在运行时丢失Gson 拿到的是LinkedTreeMap赋值给ListUserInfo时类型转换虽然不报错但里边的字段就对不上了。我踩过一次很深的坑项目里用了 Kotlin data class字段上既有SerializedName又有默认值但混淆后某个字段始终解析不出来。最后发现是-keepattributes *Annotation*没写SerializedName注解被 R8 干掉了Gson 只能退回用 Java 字段名去匹配 JSON key结果当然全乱了。4.3 release 包跑起来没问题但体积一点没降配置好混淆后满心欢喜看包体积发现和没开混淆几乎一样。这种情况基本是规则文件里有“通杀保留”比如我前面提到的-keep class ** { *; }或者你的依赖库里悄悄打进了类似的 consumer 规则。还有一个可能你开了minifyEnabled true但shrinkResources忘了开。代码压缩如期执行但资源文件仍然全部打包进去体积优化打了折扣。这种情况把shrinkResources true补上就行。另一个常见的体积问题来源是混淆确实生效了但你的包体积大头其实是本地 so 库和静态资源代码本身只占了很小比例。这种情况不用焦虑混淆对体积的影响本身就因项目而异。安全价值才是主要目的。4.4 构建时一直卡在 Gradle 下载或依赖解析失败这个案例严格说不算混淆问题但我在配混淆时被它卡过太多次这里值得单独拿出来说。很多人改完build.gradle准备打 release 包结果 Gradle 一直在下载发行版或者报Could not resolve Gradle:gradle:8.7之类的错误。解决思路我在 2.3 节已经详细写过改distributionUrl到国内镜像配repositories镜像源必要时用本地离线包。如果你是在 Android Studio 里首次导入项目经常会卡在 “Importing Gradle Project” 这一步本质也是 Gradle 发行版下载慢。这时候与其干等不如到 Gradle 官网或镜像站手动下载对应版本放到 wrapper 的 dists 目录下重新 sync 一次就过去了。这里还想补充一个思路不少团队在 CI 上构建时也会卡在 Gradle 下载建议提前在 CI 容器里预置好 Gradle 发行版或者把整个~/.gradle/wrapper/dists目录缓存下来能省掉大量重复下载时间。4.5 混合开发里 Flutter 报 gradle plugin 应用方式错误现在不少项目是 Flutter 混合开发Android 端靠 Gradle 打壳。我遇到过在 Flutter 工程里跑flutter build apk --release时Gradle 直接报错You are applying Flutters main Gradle plugin imperatively using the apply script method...这个报错和混淆没有直接关系但它会阻止你构建 release 包进而无法验证混淆效果。它是新版 Android Gradle Plugin 开始要求使用pluginsDSL 方式引入插件老式apply plugin:写法被标记为不兼容。解决方式是修改android/app/build.gradle开头把apply plugin: com.android.application改成plugins { id com.android.application }注意pluginsDSL 不能写在apply plugin: com.flutter.gradle后面顺序要理清。改完后重新flutter clean flutter build apk --release就能继续跑。这个案例放在这里是想提醒大家混合开发项目的构建链路更长任何一环出问题都可能让你误以为是混淆配置的锅先保证 release 构建链路完整再谈验证混淆效果。问题现象可能原因定位方法解决方向启动闪退ClassNotFoundException反射加载的类被混淆崩溃堆栈定位类名查 mapping 文件为反射目标类加 keep 规则接口数据解析后字段为 null数据模型字段被混淆注解丢失对比 release/debug 包行为检查 SerializedName 注解保留数据模型或注解字段包体积几乎没变宽松 keep 规则或资源压缩未开启检查规则文件是否有通杀 keep确认 shrinkResources 开关收紧规则开启资源压缩Gradle 构建卡顿或下载失败Gradle 发行版或依赖库下载慢查看 gradle-wrapper.properties 和 repositories 配置切换镜像源或使用离线包Flutter 构建报插件应用错误新版 AGP 不兼容 apply plugin 写法查看构建日志和 android/app/build.gradle 头部改用 plugins DSL 方式引入插件5. 混淆配置之外我建议你顺手做好的几件事5.1 把 mapping 文件纳入版本管理别裸奔线上崩溃日志永远是乱码版堆栈比如a.b.c(SourceFile:1)。没有 mapping 文件你想还原出原始类名只能靠人肉猜。就算你有-keepattributes SourceFile, LineNumberTable也只能看到文件名和行号类名依然是混淆后的。这时候 mapping 文件就是唯一的对照表。我现在的习惯是每次打 release 包后自动把app/build/outputs/mapping/release/mapping.txt复制到单独目录按版本号和构建时间命名归档同时同步到团队共享的对象存储里。这样线上出了崩溃谁都能第一时间拿到对应版本的 mapping 去还原堆栈。还原堆栈用的是 SDK 自带的 retrace 工具路径在$ANDROID_HOME/tools/proguard/bin/retrace.sh命令行retrace.sh -verbose mapping.txt stacktrace.txtWindows 下对应的是retrace.bat。输出结果就是还原后的原始类名和方法名配合行号能直接定位到代码位置。5.2 用 CI 跑一次 release 冒烟测试再发版混淆开好之后不能只在本地装个 release 包点两下没问题就完事。我见过太多次“本地能跑线上崩了”的案例因为 release 和 debug 的代码执行路径确实不一样。资源压缩可能把某个getIdentifier动态获取的资源干掉了混淆可能把某些反射目标改没了这些问题在小规模手点测试里很难暴露。如果你有 CI建议在发版流程里加一个环节构建 release 包后自动跑一遍核心用例冒烟测试至少把首页启动、登录、列表加载、详情跳转这几条主干路径走到。条件允许的话在多种屏幕尺寸和 Android 版本上做兼容性验证。没有 CI 的话人工回归时也要把 release 包当成一个独立产物认真测不要想当然。5.3 升级 AGP 或 Gradle 后一定要重新验证混淆Android 工具链更新换代很快AGP 从 7 升到 8R8 的规则行为可能发生变化。最典型的就是某些原本被保留的隐式规则被移除或者某个-keep关键字解析规则变更。所以每次升级完 Gradle 或 AGP我建议做两件事第一重新打一个 release 包看看构建日志里有没有新增的 warning。警告多的时候别只扫一眼就略过重点看跟 keep 规则相关的部分。第二用冒烟用例再跑一遍 release 包。不要觉得“上次升完没问题这次应该也没事”工具链的每个版本差异都可能埋雷。5.4 理想的开混淆节奏渐进式收敛如果你是在一个完全没有混淆配置的老项目上开工我不建议一上来就铺开全部 keep 规则。推荐做法是先只开minifyEnabled true用一个比较宽的规则文件兜底比如保留含Keep注解的类和 manifest 中的组件然后构建一次 release 包。接下来让测试团队或你自己用 release 包每天正常使用发现问题就补一条规则同时记录到专门的混淆规则维护文档里。坚持一两周规则会越来越收敛最终稳定在一个既能压缩代码又能保证正常功能的平衡点。这个过程虽然慢但比憋大招“一次配完、上线爆炸”要稳妥得多。我自己的经验是每一条新增的 keep 规则都要写下理由格式可以是# 保留 XxxManager因为路由模块通过 Class.forName 反射加载该类 -keep class com.example.router.XxxManager { *; }这样等三个月后你自己再回来维护这批规则会非常轻松。5.5 接入崩溃监控平台后别忘了配置自动反混淆现在大部分团队都会接入崩溃监控平台比如腾讯的 Bugly 或者其他平台。这类平台通常支持上传 mapping 文件这样它上报的堆栈会直接展示还原后的类名。我踩过一次坑mapping 文件没传平台上一堆乱码堆栈想定位问题只能手动 retrace效率极低。上传 mapping 的时机最好和发版节奏对齐。你在本地构建完 release 包后确认 mapping 文件正确生成然后上传到平台的对应版本下面。自动化做得好的团队会在 CI 脚本里把上传步骤也带上这样每次发包都自动关联不用人肉操作。结尾再啰嗦几句实在话代码混淆这块配置说难不难但确实是个“细节里见真章”的活儿。我碰过的项目中有的因为缺了一条-keepattributes Signature导致线上接口大面积解析失败有的因为规则文件里一条通杀 keep 让包体积完全没降下来还有的因为没归档 mapping 文件导致崩溃堆栈根本没法还原。这些坑单个看都不复杂平时不留意的话真正踩到的时候就特别耽误事。我自己现在的固定流程是新项目第一版 release 就把minifyEnabled true和shrinkResources true打开规则文件从基础四件套开始跑通后再根据实际崩溃情况逐步收紧。发版前至少完整跑一遍 release 包的核心链路mapping 文件永远归档留底。这套流程看着不起眼但能帮我省掉太多线上翻车的麻烦了。你要是正好在给项目配混淆不妨把这份清单也复制走祝你的 release 包既能“瘦身”又能“防盗”。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价