我遇到了一个问题,我认为可以追溯到 ViewModel 范围。我在同一个 Activity 下的同一个导航图中有两个 fragment 。第一个 fragment LoginFragment 实例化一个 UserDataViewModel 并将它的一些数据填充到一个名为 UserData 的对象中

第二个 fragment TodayFragment 应该选择 UserDataViewModel 并使用已经填充的属性来做进一步的工作。但是,当 TodayFragment 尝试访问 UserData 对象时,该对象为空。需要强调的是,UserDataViewModel 仍然具有相同的内存地址,但问题是它丢失了对其数据值的引用。

我已经把它去掉了,这样数据只是从 SharedPreferences 中提取一个字符串并使用 GSON 解析它。

登录 fragment :

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()
}

今天 fragment
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
        })
}

UserDataViewModel.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)
                }
        }
    }
}

奇怪的地方在于 View 模型中的 getUserData() 方法。当我在这些行上设置断点时:
return userDataMutableLiveData<UserData>().apply { loadUserData() }return@withContext useCase.loadUserData(USER_UNKNOWN)
那些断点都命中了LoginFragment。但是当它进入 TodayFragment 时,它碰到了 View 模型中的 return userData ,然后立即跳回到 TodayFragment 内部的观察者, View 模型内部的值出现空指针异常。

我认为这在 View 模型本身被实例化的程度上是有道理的,但为什么 View 模型中的值是空的?

出于调试目的,我对来自用例和存储库的响应进行了硬编码,以确保在 ViewModel 中填充了一个值。

来自 LoginFragment 的屏幕截图
android - ViewModel 跨同一 Activity 中的 Fragment 的范围-LMLPHP

来自 TodayFragment 的屏幕截图(扩展 AbstractWorkoutFragment)
请注意 mData 现在如何为空,但 ViewModel 是相同的。
android - ViewModel 跨同一 Activity 中的 Fragment 的范围-LMLPHP

最佳答案

在这种情况下,您可以使用 navGraphViewModels() 属性委托(delegate)从 fragment 目标中检索 ViewModel,通过这样做,您可以访问“附加”到您的导航图的 View 模型

所以通常你可以在 LoginFragmentTodayFragment 中使用以下内容

private val userDataViewModel: UserDataViewModel by navGraphViewModels(R.id.youNavGraphID)

但是在您的情况下,您在初始化时将参数传递到 View 模型中,因此您还必须传递 ViewModelProvider.Factory
private val sharedViewModel: BookViewModel by navGraphViewModels(R.id.navGraph, ::viewModelProducer)

fun viewModelProducer(): ViewModelProvider.Factory {
    return object : ViewModelProvider.Factory {
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
            return UserDataViewModelFactory(prefs, dataFetcherService) as T
        }
    }
}

而不仅仅是
 userDataViewModel = activity?.run {
    ViewModelProviders
        .of(this, UserDataViewModelFactory(prefs, dataFetcherService))
        .get(UserDataViewModel::class.java)
} ?: throw Exception("Invalid Activity")

--- 更新 ---

我刚刚面临同样的情况。我会保留我以前的答案,但我相信下面的解决方案应该可以解决您的问题。

当您初始化 Activity/Fragments 范围 View 模型时,您不应在 this 中传递 ViewModelProviders.of() ,因为这意味着 Fragment 本身而不是 Activity 。
 userDataViewModel = activity?.run {
    ViewModelProviders
        .of(this, UserDataViewModelFactory(prefs, dataFetcherService))
        .get(UserDataViewModel::class.java)
} ?: throw Exception("Invalid Activity")

相反,您应该按如下方式使用该 Activity 。
userDataViewModel = activity?.run {
    ViewModelProviders
        .of(it, UserDataViewModelFactory(prefs, dataFetcherService))
        .get(UserDataViewModel::class.java)
} ?: throw Exception("Invalid Activity")

10-08 17:54