F.M.
F.M.

Reputation: 285

ViewModel and LiveData persist in destroyed fragment causing NPE on second access to fragment

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?

Upvotes: 0

Views: 80

Answers (1)

TheLibrarian
TheLibrarian

Reputation: 1898

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.

Upvotes: 1

Related Questions