I'm using a pattern that I've used a few times before to instantiate a ViewModel
object. In this case, the data is saved as a string in SharedPreferences
. I just need to read that string, parse it to the correct object, and assign that object as the value to my view model.
But when I do the assignment, I create an infinite loop.
class UserDataViewModel(private val prefs: SharedPreferences): ViewModel() {
val userData: MutableLiveData<UserData> by lazy {
MutableLiveData<UserData>().also {
val userDataString = prefs.getString(Authenticator.USER_DATA, "")
val ud = Gson().fromJson(userDataString, UserData::class.java)
userData.value = ud // infinite loop is here
}
}
fun getUserData(): LiveData<UserData> {
return userData
}
}
This is in onCreateView()
of the fragment that keeps the reference to the ViewModel
:
userDataViewModel = activity?.run {
ViewModelProviders
.of(this, UserDataViewModelFactory(prefs))
.get(UserDataViewModel::class.java)
} ?: throw Exception("Invalid Activity")
userDataViewModel
.getUserData()
.observe(this, Observer {
binding.userData = userDataViewModel.userData.value
})
FWIW, in the fragment, I have break points on both getUserData()
and on binding.userData...
. The last break point that gets hit is on getUserData()
.
I don't see where the loop is created. Thanks for any help.
The userData
field is only initialized once the by lazy {}
block returns. You're accessing the userData
field from within the by lazy {}
block and that's what is creating the loop - the inner access sees that it hasn't finishing initializing, so it runs the block again..and again and again.
Instead, you can access the MutableLiveData
you're modifying in the also
block by using it
instead of userData
, breaking the cycle:
val userData: MutableLiveData<UserData> by lazy {
MutableLiveData<UserData>().also {
val userDataString = prefs.getString(Authenticator.USER_DATA, "")
val ud = Gson().fromJson(userDataString, UserData::class.java)
it.value = ud
}
}