0

I have a fragment which displays a list in recyler view (chargeHistoryList). all the code is written in onActivityCreated, so this gets called only once when the fragment is called. onResume method is pretty blank at the moment.

Issue I am having is, when I initially open this fragment ChargeHistoryFragment recyclerview is loaded correctly with data. when I put the app in the background and comeback to it the recyclerview is empty, no data is shown.

I tried adding the code in onViewCreated but that also gets called only once and onresume is the only lifecylce method thats gets called when the app goes in background and comes back to foreground.

How can I make the adapter load with data when only onResume is called

ChargeHistoryFragment

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    return inflater.inflate(R.layout.charge_history_fragment, container, false)
}

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)

    baseActivity?.updateAppBar(title = screenTitle)

    chargeHistoryList.setHasFixedSize(true)
    chargeHistoryList.addItemDecoration(VerticalSpaceItemDecoration(8))

    onChargeSelectListener = activity as OnChargeSelectListener

    var chargeHistoryAdapter = ChargeHistoryDataAdapter(dateFormatter,
        decimalFormatter,
        timeFormatter,
        onItemClickListener = this,
        currencies = listOf())
    chargeHistoryList.adapter = chargeHistoryAdapter

    viewModel.getCurrencies().observe(owner = this) { resource ->
        when (resource.status) {
            SUCCESS -> {
                resource.data?.let {
                    chargeHistoryAdapter = ChargeHistoryDataAdapter(dateFormatter,
                        decimalFormatter,
                        timeFormatter,
                        onItemClickListener = this,
                        currencies = it)
                    chargeHistoryList.adapter = chargeHistoryAdapter
                }
            }
            LOADING, ERROR -> { /** DO NOTHING **/ }
        }
    }

    lifecycleScope.launch {
        viewModel.chargeHistory.collectLatest {
            chargeHistoryAdapter.submitData(it)
        }
    }

    chargeHistoryAdapter.addLoadStateListener { loadState ->
        if (loadState.source.refresh is LoadState.NotLoading && loadState.append.endOfPaginationReached && chargeHistoryAdapter.itemCount < 1) {
            setEmptyViewVisibility(true)
        } else {
            setEmptyViewVisibility(false)
        }
    }
}

   override fun onResume() {
    super.onResume()
}

Adapter

 class ChargeHistoryDataAdapter(
    private val dateFormatter: DateFormatter,
    private val decimalFormatter: DecimalFormat,
    private val timeFormatter: TimeFormatter,
    private val currencies: List<CurrencyDB>,
    private val onItemClickListener: ListAdapterBase.OnItemClickListener<ChargingSessionDB?>

) : PagingDataAdapter<ChargingSessionDB, ChargeSessionViewHolder>(ChargingSessionDiffUtilCallback()){
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChargeSessionViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.charge_history_list_item, parent, false)
        return ChargeSessionViewHolder(view, dateFormatter, decimalFormatter, timeFormatter, currencies)
    }

    override fun onBindViewHolder(holder: ChargeSessionViewHolder, position: Int) {
        holder.bindTo(getItem(position), position, onItemClickListener)
    }
}

Thanks in advance R

Edit after @Tenfour04 suggestion

onActivityCreated

viewModel.getCurrencies().observe(owner = viewLifecycleOwner) { resource ->
        when (resource.status) {
            SUCCESS -> {
                Log.d("issueHappening", "viewModel.getCurrencies()")
                resource.data?.let {
                    chargeHistoryAdapter = ChargeHistoryDataAdapter(dateFormatter,
                        decimalFormatter,
                        timeFormatter,
                        onItemClickListener = this,
                        currencies = it)
                    chargeHistoryList.adapter = chargeHistoryAdapter
                }
            }
            LOADING, ERROR -> { /** DO NOTHING **/ }
        }
    }

    viewLifecycleOwner.lifecycleScope.launch {
        viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
            viewModel.chargeHistory.collectLatest {
                Log.d("issueHappening", "viewModel.chargeHistory")
                chargeHistoryAdapter.submitData(it)
            }
        }
    }
BRDroid
  • 3,920
  • 8
  • 65
  • 143

1 Answers1

0

You aren't collecting on the right lifecycle. This:

lifecycleScope.launch {
    viewModel.chargeHistory.collectLatest {
        chargeHistoryAdapter.submitData(it)
    }
}

Should be:

viewLifecycleOwner.lifecycleScope.launch {
    viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { 
        viewModel.chargeHistory.collect {
            chargeHistoryAdapter.submitData(it)
        }
    }
}

or:

viewModel.chargeHistory
    .flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.STARTED)
    .onEach { chargeHistoryAdapter.submitData(it) }
    .launchIn(viewLifecycleOwner.lifecycleScope)

I changed collectLatest() to collect() because passing a list to the adapter is a trivial action that will return instantly and has no possible way of being cancelled or needing to be cancelled (I'm assuming it is a non-blocking, non-suspending function). If your collecting takes a long time and is cooperative with cancellation, then it would make sense to use collectLatest. There is no onEachLatest, but you could possibly use transformLatest in place of onEach if you needed it.

Likewise with your LiveData observer, you should observe with the viewLifeCycleOwner, not the Fragment itself.

viewModel.getCurrencies().observe(owner = viewLifecycleOwner) { resource ->
Tenfour04
  • 83,111
  • 11
  • 94
  • 154
  • hello @Tenfour04 thank you for responding, I have implemented what you have said, both the methods `viewModel.getCurrencies()` and `viewModel.chargeHistory` are called once I resume, as per logs but still the recycler view is blank. When I debug, it does not go into `onCreateViewHolder` of `ChargeHistoryDataAdapter` any idea why this might be happening I have updated what I have implemented – BRDroid Mar 02 '22 at 13:03
  • I don’t see other problems. `resource.data?.let` is suspicious. Maybe `data` is null. Why is it possible for the data to be null if you always expect it to be available? You might try putting this code in `onViewCreated()` instead. I’ve never used `onActivityCreated()` and maybe I’m missing some detail about the Fragment lifecycle and this isn’t an appropriate place to set up observers. Step through with the debugger in your observers. – Tenfour04 Mar 02 '22 at 13:17
  • okay thank you I will get back to you, thank you so much for helping out on this – BRDroid Mar 02 '22 at 13:19