RecyclerView -> notifyDataSetChange not working when app is relaunched

90 Views Asked by At

My Android app has this flow of screens when launched:

Splash -> Chat Groups > Chat Screen (showing chat messages)

On Chat Screen I have my Custom RecyclerView Implemented.

On fresh launch (or after killing the app), I go to Chat Screen, it loads previous messages fine, and new incoming message is also seen when u r on this screen.

Now if I press Android's Back button few time to exit the app, and then relaunch the app and go to Chat Screen, previous messages appear fine BUT the new incoming message is not visible.

Important thing is, even if I don't go to Chat Screen the first time and close the app from Groups Screen, then relaunching and going to Chat Screen again causes the problem and I dont see new incoming Chat messages.

I have debugged it and all code is being executed fine. The incoming message is added to the list of RecycleView, and notifyDataSetChange() is being called, but onBindViewHolder() is not being called in this case, and that's why the list doesn't get updated.

The code is pretty lengthy, but if u still need to see it then I'll try to add. This is driving me crazy, I am pretty sure it's a bug in Android.

If u can propose a workaround, like clearing the RecyclerView or Adapter somehow that it gets to same state as when i Kill the app and launch..

Here is the code:

//Initialize Recycler view
  mMessageRecycler = findViewById(R.id.recyclerview_message_list)
  mMessageRecycler?.layoutManager = LinearLayoutManager(this)
....   
       if (messagesAdapter == null) {
            messagesAdapter = NewMessageListAdapter(this)
            mMessageRecycler?.adapter = messagesAdapter
        }
//Adapter
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position:Int) {
        val message = messageList[position]
        (holder as ReceivedMessageHolder).bind(message)
    }

    override fun getItemCount(): Int {
        return messageList.size
    }
...

//add new chat message. Breakpoint does hit this code
   messageList.add(newMessage)
   notifyDataSetChanged()
2

There are 2 best solutions below

7
Martin Marconcini On

As it's impossible to tell what is going on without looking at a bigger picture, I'll give you a few pointers.

a. I suggest you attempt to use ListAdapter<T, K> as it forces you to provide a DiffUtil.ItemCallback implementation. This will allow you to avoid calling the expensive and extremely inefficient notifyDataSetChanged(); instead you will call adapter.submitList(...) and supply a List<T> with your data.

K is the ViewHolder type. Usually you use RecyclerView.ViewHolder (if I correctly recall or if you only have one viewHolder type then you can couple that there and use it directly. Otherwise you'll just have to "cast" your ViewHolders to be able to call their "bind" method.

b. As for "it doesn't work when I get back", this is a bit harder to detect, as we haven't seen how/where/when you fetch this data; are you using Android Coroutines? Are the list of messages stored in a repository relying on memory or database persistence? who updates this list?

As you can see, there are a few outstanding questions that we (the readers) cannot possibly infer given the information you've provided.

If you want to see the simplest example of a RecyclerView with ListAdapter, I often tend to link this sample I wrote because it shows how to put all pieces together.

c. You shouldn't need to do if (adapter == null) { // create it and set it } either. You can have:


class YourActivityOrFragment : ... {

     private val adapter = YourAdapter()

     override fun onViewCreated(...) {
         yourRecyclerView.adapter = adapter
     }

You can later set the data in the adapter once you have it, there's no need to delay the creation of this. If you're going to use a LinearLayoutManager, remember you can also set it directly in XML and avoid writing the code.

0
M. Usman Khan On

Finally I found the problem! It was due to some memory leak and threads issue. At some point in the code i was re-initializing my Mqtt class, without checking if it is not null. So I just added a null check and it fixed

if (mqttMy == null) // added
    mqttMy = MqttMy(context)