Search code examples
androidkotlinmvvmviewmodelobservers

Recyclerview Duplicate Values with LiveData onBackPressed in Fragment


I have a recyclerview with load more, and I can't store the values in the database. So when I load the data, everything works perfectly. The problem occurs when I navigate to another fragment, and click onBackPressed, the onChanged of the observer is being called again and it is giving me the last values called from the API. Then as you can see in the code below, they are automatically being added to the list and published in UI

 productsViewModel.productsObject.observe(viewLifecycleOwner, Observer { result ->
        if (result.status != Status.LOADING) {
            (activity as MainActivity?)!!.dismissProgressDialog(getView())
            if (result.status == Status.SUCCESS && result.data != null) {
                val productsResult = parseObject(result.data.asJsonObject)
                if (!productsResult.isNullOrEmpty()) {
                    products.addAll(productsResult)
                    productAdapter.submitList(products)
                    progressBar.visibility = View.GONE
                    numberSearch.text = products.size.toString() + "/" + total + " " + resources.getString(R.string.items_found)
                } else if (result.status == Status.ERROR) {
                    if (result.message.isNullOrBlank())
                        Utils.showSnackBar(requireContext(), view, getString(R.string.something_wrong_try_again), true)
                    else
                        Utils.showSnackBar(requireContext(), view, result.message, true)
                }
            }
        }
    })

Solution

  • I think that you are not observing your result correctly.

    For example, if the value of result is Status.LOADING what happens? After you enter that block, maybe there are other cases that you are not handling. We don't know because there is no other when or else if block.

    Another way more "clean" would be:

    productsViewModel.productsObject.observe(viewLifecycleOwner, Observer { result ->
        when(result.status) {
            Status.LOADING -> manageLoading()
            Status.SUCCESS -> manageSuccess()
            Status.ERROR -> manageError()
            else -> manageBaseCase()
        }
    }
    

    Thus, it is not all embedded in a unique if block making the management more understandable.

    In your Fragment you should manually send an event to "reset" the actual state of the LiveData so that you will be sure that there won't be duplicate items.

    Your Fragment may look something like this:

    class ProductsFragment : Fragment() {
        override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
            return inflater.inflate(R.layout.product_fragment, container, false)
        }
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            observe()
            productsViewModel.send(Event.Load)
        }
    }