android - ViewModel 在同一个 Activity 中跨 Fragment 的作用域
问题描述
我有一个问题,我认为可以追溯到ViewModel
范围界定。我在同一个活动下的同一个导航图中有两个片段。第一个片段,LoginFragment
实例化 aUserDataViewModel
并将它的一些数据填充到一个名为的对象中UserData
第二个片段TodayFragment
应该拾取UserDataViewModel
并使用已经填充的属性来做进一步的工作。但是,当TodayFragment
尝试访问该UserData
对象时,该对象为空。需要强调的是,UserDataViewModel
仍然有相同的内存地址,但问题是它失去了对其数据值的引用。
我已将其剥离,以便数据只是从中提取字符串SharedPreferences
并使用 GSON 对其进行解析。
登录片段:
private lateinit var userDataViewModel: UserDataViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
MyApplication.appComponent?.inject(this)
prefs = PreferenceManager.getDefaultSharedPreferences(context)
userDataViewModel = activity?.run {
ViewModelProviders
.of(this, UserDataViewModelFactory(prefs, dataFetcherService))
.get(UserDataViewModel::class.java)
} ?: throw Exception("Invalid Activity")
userDataViewModel.getUserData()
}
今日片段
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
prefs = PreferenceManager.getDefaultSharedPreferences(activity)
userDataViewModel = activity?.run {
ViewModelProviders
.of(this, UserDataViewModelFactory(prefs, dataFetcherService))
.get(UserDataViewModel::class.java)
} ?: throw Exception("Invalid Activity")
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
userDataViewModel
.getUserData()
.observe(this, Observer {
clubId = it.club_id // <-- this is where it crashes since 'it' is null
})
}
用户数据视图模型.kt
class UserDataViewModel(
prefs: SharedPreferences,
dataFetcherService: DataFetcherService)
: ViewModel() {
private val useCase = UserDataUseCase(prefs, dataFetcherService)
val userData: MutableLiveData<UserData> by lazy {
MutableLiveData<UserData>().apply { loadUserData() }
}
fun getUserData(): LiveData<UserData> {
return userData
}
private fun loadUserData() {
viewModelScope.launch {
withContext(Dispatchers.IO) {
try {
return@withContext useCase.loadUserData(USER_UNKNOWN)
} catch (t: Throwable) {
return@withContext null
}
}
?.let {
userData.postValue(it)
}
}
}
}
奇怪的地方在于getUserData()
视图模型中的方法。当我在这些行上设置断点时:
return userData
MutableLiveData<UserData>().apply { loadUserData() }
return@withContext useCase.loadUserData(USER_UNKNOWN)
这些断点都命中了 LoginFragment。但是当它进入 TodayFragment 时,它会return userData
在视图模型中命中,然后立即跳回到 TodayFragment 内部的观察者,并带有针对视图模型内部值的空指针异常。
我认为这在视图模型本身被实例化的程度上是有道理的,但是为什么视图模型内部的值是 null 呢?
出于调试目的,我对来自用例和存储库的响应进行硬编码,以确保在ViewModel
.
TodayFragment 的屏幕截图(扩展 AbstractWorkoutFragment)
请注意mData
现在如何为空,但 ViewModel 是相同的。
解决方案
在每个片段中,您都将获得ViewModel
这种方式的实例:
ViewModelProviders
.of(this, UserDataViewModelFactory(prefs, dataFetcherService))
注意this
,指向哪个Fragment.this
。这意味着,作为LifecycleOwner
您提供的实例Fragment
。因此,每个 Fragments 都会创建自己的新实例UserDataViewModel
.
相反,您真正想要的是重用 的相同实例ViewModel
,这意味着LifecycleOwner
您应该传入托管活动:
ViewModelProviders
.of(requireActivity(), UserDataViewModelFactory(prefs, dataFetcherService))
这样,您将ViewModel
在两者中获得相同的实例Fragments
。
推荐阅读
- c# - 如何获得提交表单 ASP.NET VS 2019 的按钮
- visual-studio - 使用 WSL 2 安装 Visual Studio Code
- javascript - Axios 不发送 cookie 数据,即使使用 withCredentials:true
- python - 读取文件,Python
- rule-engine - 如何在 CLIPS 中存储一个函数,如 < 或 >= 作为稍后评估它的值?
- flutter - 如何从消费者中的提供者生成密钥?
- arrays - 在 Google 表格中将两列表转换为一个列表
- laravel - 在本地网络上共享代客泊车站点
- python - 如何定位列中值 1 之前的 5 行和之后的 5 行
- php - 如何从 WP 中的 ACF Image 字段的数组中获取信息?