核心总原则
- 能混淆就尽量混淆:缩小包体积、增加逆向难度、删除无效代码;
- 只要被「外部反射 / 系统反射 / 跨进程 / JS/JNI/ 序列化」调用的代码,一律不能混淆,必须 -keep;
- 纯内部逻辑、仅自己代码直接调用、无反射、无跨外部调用 → 放心混淆。
一、可以正常混淆(不用 keep)的代码
满足全部条件:
- 只在自己 Java/Kotlin 代码里直接硬编码调用,不存在反射;
- 不在 AndroidManifest、XML、JS、JNI、路由框架、序列化工具中被间接加载;
- 没有被第三方框架通过注解反射扫描。
举例:
- 普通工具类、内部业务逻辑类、私有方法、私有接口实现;
- 仅内部 new、直接调用的普通方法;
- 仅本类内部使用的私有内部类;
- 不参与 JSON 序列化、不做 Parcelable/Serializable 传递的临时数据类。
好处:R8/ProGuard 会自动改名、内联、删除无用分支,减小安装包。
示例场景:
kotlin
// 纯内部工具,全可混淆
class CalcUtil {
fun add(a:Int,b:Int) = a+b
}
不需要任何 keep 规则。
二、绝对不能混淆,必须 keep 的代码(高频踩坑区)
1. 系统反射创建的类(四大组件、自定义 View)
系统通过字符串全类名反射实例化,混淆改名后直接 ClassNotFound 闪退
- Activity、Service、BroadcastReceiver、ContentProvider
- 自定义 View(xml 通过 class/ViewGroup 反射加载)
- Application
2. 序列化 / JSON 实体类(Gson/ Moshi/FastJson/Parcelable)
序列化框架通过反射 get/set 字段,字段名混淆后解析失败、数据全空
- 网络返回 Bean、本地存储实体
- Parcelable、Serializable 序列化对象(页面传参)
3. 反射相关(自己手写反射、框架反射)
- 手写
Class.forName、getMethod、getField操作的类 / 方法 / 字段; - 事件总线 EventBus、LiveDataBus、路由框架(ARouter、WMRouter);
- 注解驱动框架(APT、动态代理、Retrofit 接口)。
4. JNI 交互类与 native 方法
C/C++ 层写死 Java 方法名,混淆后找不到方法崩溃
java
运行
// native 方法不能改名
native void test();
5. WebView JS 交互接口
JS 用字符串调用 Java 方法,混淆后方法找不到
java
运行
@JavascriptInterface
public void callAndroid(){}
6. 动态下发配置、热修复、插件化相关类
插件 / 热更通过字符串加载类,混淆会找不到类。
7. 回调、接口被第三方 SDK 反射调用
第三方 SDK 通过反射加载你的实现类,必须 keep。
三、接口单独说明:接口要不要混淆?
分两种情况:
情况 1:接口只在内部代码直接实现、直接调用 → 可混淆
kotlin
interface ICalc
class CalcImpl : ICalc
// 代码里直接 CalcImpl() as ICalc 调用,无反射,随便混淆
情况 2:接口被反射 / 框架扫描实例化 → 接口 + 实现类都要 keep
示例:
- Retrofit 接口;
- 框架
Class.newInstance()反射获取接口实现; - 注解框架扫描所有实现类。
proguard
# 保留接口及所有实现类
-keep interface com.xxx.api.IMyApi
-keep class * implements com.xxx.api.IMyApi {*;}
四、方法单独说明:哪些方法不能混淆
- XML 中
android:onClick="xxx"点击方法; - JS 交互
@JavascriptInterface方法; - Native 对应的 Java native 方法;
- 反射工具调用的 set/get、自定义回调方法;
- EventBus @Subscribe 订阅方法;
- Parcelable 的 CREATOR、枚举 values ()/valueOf ()。
普通私有 / 公共业务方法,代码直接调用,无反射 → 随便混淆。
五、两种保守方案(项目常用)
方案 1:精准 keep(推荐,混淆最大化,包最小)
只对必须不混淆的类加 -keep,其余全部混淆示例:
proguard
# 只保留网络实体
-keep class com.xxx.bean.** { *; }
# 只保留四大组件
-keep public class * extends android.app.Activity
# 只保留JS交互类
-keep class com.xxx.JsBridge {<methods>;}
优点:逆向难度最高,包体积最优;缺点:需要自己梳理所有反射类,漏写就崩溃。
方案 2:全包不混淆(简单懒人方案,不推荐上线)
proguard
-keep class com.xxx.** {*;}
适用:快速调试、临时打包测试;缺点:所有类名都明文暴露,极易被逆向,包体积更大。
六、快速判断口诀
- 代码直接 new / 直接点方法调用 → 随便混淆;
- 通过字符串名字找到类 / 方法(反射、XML、Manifest、JS、JNI、路由、JSON 解析)→ 必须 keep;
- 第三方框架、系统服务自动实例化的类 → 必须 keep;
- 纯内部工具、私有逻辑、无外部反射依赖 → 放心混淆。
七、常见误区纠正
- 误区:自己写的业务类全部要 keep,不然会报错纠正:只要没有反射调用,混淆完全没问题,R8 会自动处理调用关系。
- 误区:接口必须 keep纠正:仅反射使用的接口才需要,内部直接调用的接口可混淆。
- 误区:public 方法都要 keep纠正:public 只是访问权限,只要不是反射调用,照样可以混淆改名。