Home > OS >  How to prevent data duplication caused by LiveData observation in Fragment?
How to prevent data duplication caused by LiveData observation in Fragment?

Time:01-31

I'm subscribed to an observable in my Fragment, the observable listens for some user input from three different sources.

The main issue is that once I navigate to another Fragment and return to the one with the subscription, the data is duplicated as the observable is handled twice.

What is the correct way to handle a situation like this?

I've migrated my application to a Single-Activity and before it, the subscription was made in the activity without any problem.

Here is my Fragment code:

@AndroidEntryPoint
class ProductsFragment : Fragment() {
    @Inject
    lateinit var sharedPreferences: SharedPreferences

    private var _binding: FragmentProductsBinding? = null
    private val binding get() = _binding!!

    private val viewModel: ProductsViewModel by viewModels()
    private val scanner: CodeReaderViewModel by activityViewModels()

    private fun observeBarcode() {
        scanner.barcode.observe(viewLifecycleOwner) { barcode ->
            if (barcode.isNotEmpty()) {
                if (binding.searchView.isIconified) {
                    addProduct(barcode) // here if the fragment is resumed from a backstack the data is duplicated.
                }

                if (!binding.searchView.isIconified) {
                    binding.searchView.setQuery(barcode, true)
                }
            }
        }
    }

    private fun addProduct(barcode: String) {
        if (barcode.isEmpty()) {
            return
        }

        viewModel.insert(barcode)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewModel.start(args.documentId)

        if (args.documentType == "Etichette") {
            binding.cvLabels.visibility = View.VISIBLE
        }

        initUI()
        observe()
    }

    private fun observe() {
        observeBarcode()
        observeProducts()
        observeLoading()
        observeLast()
    }
}

CodePudding user response:

Every time you call

scanner.barcode.observe(viewLifecycleOwner){
}

You are creating a new anonymous observer. So every new call to observe will add another observer that will get onChanged callbacks. You could move this observer out to be a property. With this solution observe won't register new observers.

Try

class property
val observer = Observer<String> { onChanged() }

inside your method
scanner.barcode.observe(viewLifecycleOwner, observer)

Alternatively you could keep your observe code as is but move it to a Fragment's callback that only gets called once fex. onCreate(). onCreate gets called only once per fragment instance whereas onViewCreated gets called every time the fragment's view is created.

CodePudding user response:

Unfortunately, LiveData is a terribly bad idea (the way it was designed), Google insisted till they kinda phased it out (but not really since it's still there) that "it's just a value holder"...

Anyway... not to rant too much, the solution you have to use can be:

  1. Use The "SingleLiveEvent" (method is officially "deprecated now" but... you can read more about it here).

  2. Follow the "official guidelines" and use a Flow instead, as described in the official guideline for handling UI Events.

  • Related