Hilt注入全局单例仓库问题:ViewModel重建后_user为空
排查思路与解决方案
嘿,我来帮你拆解这个问题~首先你已经通过调试确认注入的UserRepository是同一实例,那Hilt的单例注入逻辑肯定没问题,问题出在这个单例实例的_user状态丢失上,我们从几个核心方向排查:
1. 优先确认:App进程是否被系统销毁重建
这是Android开发中全局状态丢失最常见的原因:
- 当App处于后台且系统内存不足时,系统会主动杀死进程;等你回到App时,Application会重新初始化,Hilt的
ApplicationComponent里的所有单例(包括你的UserRepository)都会被全新创建,这时候_user自然回到初始的null状态。 - 验证方法:给
UserRepository的构造函数加日志追踪实例创建:
测试步骤:把App切到后台,用开发者选项的「终止应用」手动杀死进程,再重新打开App,查看日志是否打印了新的哈希值。如果是,那就是进程重建导致的状态丢失。@Singleton class UserRepository @Inject constructor() : UserService { init { Log.d("UserRepoDebug", "新的Repository实例创建:${this.hashCode()}") } // ... 原有代码 } - 解决办法:把
_user的状态持久化,比如用DataStore、SharedPreferences或者Room数据库,在getUser()时先从存储读取,fetchUser()成功后写入存储。
2. 检查是否有隐藏逻辑修改了_user为null
虽然你贴的代码里没有,但实际项目中可能存在你没注意到的逻辑:
- 比如有没有
logout()方法会执行_user = null?或者其他业务模块不小心修改了这个字段? - 验证方法:给
_user字段加监听日志,追踪它的修改轨迹:
这样就能精准定位@Volatile private var _user: User? = null set(value) { field = value Log.d("UserRepoDebug", "_user被更新为:${value?.name ?: "null"}") }_user什么时候被置为null了。
3. 排查线程安全问题
你的getUser()和fetchUser()没有做线程同步,可能存在并发访问导致的状态异常:
- 比如在
fetchUser()的2秒延迟期间,多次调用getUser()会触发重复请求,虽然正常情况下最终_user会被赋值,但如果某个请求出现异常(比如RxJava的错误回调),会不会间接导致_user被置空? - 优化方案:用RxJava的
ReplaySubject缓存用户数据,避免重复请求并保证线程安全:private val userCache: ReplaySubject<User> = ReplaySubject.createWithSize(1) override fun getUser(): Observable<User> { return if (_user == null) { fetchUser().doOnNext { _user = it userCache.onNext(it) } } else { userCache } }
4. 确认ViewModel的重建场景
ViewModel虽然能跨配置变更(比如旋转屏幕)存活,但如果是进程被杀后重建,ViewModel会被重新创建,同时注入的UserRepository也是全新实例(因为Application重建了)。这种情况下,ViewModel的SavedStateHandle只能保存自身的状态,全局的用户数据还是得靠持久化存储来留存。
内容的提问来源于stack exchange,提问作者jack_the_beast
相关产品推荐
相关产品推荐

