启用Proguard后Navigation组件引发崩溃,如何解决该问题?
我之前也碰到过几乎一模一样的情况!Navigation组件和Proguard的组合确实容易踩坑,尤其是只加了Fragment的keep规则远远不够,咱们一步步来解决这个问题:
核心原因分析
Proguard不仅会混淆类名,还会移除它认为“无用”的代码、注解和内部结构。你遇到的IllegalStateException: View does not have a NavController set,本质是混淆后系统找不到Navigation相关的核心类、导航图配置,或者你的Fragment/View和NavHost的关联被破坏了。
第一步:补充Proguard规则
把下面这些规则添加到你的proguard-rules.pro中,覆盖Navigation组件和Fragment的完整保留需求:
1. 保留Navigation核心组件和注解
# 保留Navigation框架的所有类和内部结构 -keep class androidx.navigation.** { *; } -keep class androidx.navigation.ui.** { *; } # 保留导航目的地和导航图的名称(避免混淆后无法匹配) -keepnames class androidx.navigation.NavDestination$** { *; } -keepnames class androidx.navigation.NavGraph$** { *; }
2. 保留Safe Args生成的类(如果使用了Safe Args)
如果你用了Navigation的Safe Args插件,必须保留生成的方向和参数类:
# 替换成你项目中Safe Args生成类的包路径,比如com.example.navigation -keep class com.example.navigation.** { *; } # 或者通用规则匹配所有生成的Directions和Args类 -keep class **.*Directions { *; } -keep class **.*Args { *; }
3. 完善Fragment的保留规则
你原来的规则太简单了,需要保留Fragment的构造方法、内部类和所有成员,避免混淆后破坏Fragment的生命周期和View关联:
-keep public class * extends androidx.fragment.app.Fragment { # 保留无参和带Bundle的构造方法 public <init>(); public <init>(android.os.Bundle); # 保留所有成员和方法 *; } # 如果用了ViewBinding,保留对应的绑定类(替换成你的databinding包) -keep class com.example.databinding.** { *; }
第二步:优化代码中NavController的获取方式
你当前用Navigation.findNavController(mView)的方式容易出问题,因为混淆后View的关联可能被破坏,或者View还没完全attach到NavHost。建议换成更可靠的方式:
把:
Navigation.findNavController(mView).navigate(R.id.action_aFragment_to_bFragment, bundle, null)
替换成:
// 通过当前Fragment直接查找NavHost的NavController NavHostFragment.findNavController(this).navigate(R.id.action_aFragment_to_bFragment, bundle, null)
这种方式不需要依赖View,直接通过Fragment的关联关系找到NavController,稳定性更高,也能规避混淆带来的View识别问题。
最后验证
添加完规则并修改代码后,重新打包发布版本,测试崩溃率应该会回到正常水平。如果还有个别崩溃,可以检查是不是有其他自定义的Navigation扩展类没被保留,或者某些动态添加NavHost的代码被混淆了。
内容的提问来源于stack exchange,提问作者c-an

