I have a ViewModel that retrieves data from a single RxJava Observable using a Disposable.
internal class MyViewModel: ViewModel() {
internal var disposable: Disposable? = null
internal var myMutableLiveData= MutableLiveData<List<...>?>()
internal fun getData(myParam: String) {
disposable = (ApiServiceClient.createApiService().getDataFromAPI(
myParam
)).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(
{ response -> myMutableLiveData.postValue(response) },{ myMutableLiveData.postValue(null) }
)
}
}
In my fragment, I observe myMutableLiveData in onViewCreated to retrieve the data. In onDestroyView of the fragment, I release resources.
internal class MyFragment: Fragment() {
private var _binding: FragmentMyBinding? = null
private val binding get() = _binding!!
private val viewModel: MyViewModel by viewModels()
override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View {
_binding = FragmentMyBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.myMutableLiveData.observe((context as MyActivity)) { data ->
if (data == null) // Error
else {
// Success, use binding
}
}
viewModel.getData("text")
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
viewModel.disposable.dispose()
}
}
The first time I choose the fragment MyFragment, everything works fine. However, when I switch to another fragment within the same activity (MyFragment pass through onDestroyView, onDestroy and onDetach) and then I return to MyFragment (onCreate, onCreateView, onViewCreated), it seems as if the ViewModel is still there, causing the observation to happen before the binding class is reconstructed, resulting in a NullPointerException.
Why this? What is the correct way to manage the lifecycle of Fragment-ViewModel-Disposable?
While there might be some niche use cases to use Activity
as the owner for the observer - your case seems to not be one.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.myLiveData.observe(viewLifecycleOwner) { data ->
if (data == null) {
// Error
} else {
// Success, use binding
}
}
viewModel.getData("text")
}
Also, it is not a good idea to expose mutable LiveData
.
private val _myMutableLiveData = MutableLiveData<List<...>?>()
internal val myLiveData = _myMutableLiveData.asLiveData()
With a note that LiveData
might return null
if it is not being observed/hasn't started yet.