-1

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()
M. Usman Khan
  • 3,689
  • 1
  • 59
  • 69

2 Answers2

1

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.

Martin Marconcini
  • 26,875
  • 19
  • 106
  • 144
  • Yes, it's very hard to show the bigger picture. Looks like a bug in Android honestly. I have implemented RecyclerView with adapter several times. I guess I'll have to replace it with ListAdapter as a workaround, thanks for the lead. – M. Usman Khan Jun 17 '22 at 10:22
  • I tried converting it to ListAdapter, and used the method submitList() instead of notifyDataSetChanged(), but got the same issue. Works on first launch but fails on seconds launch. – M. Usman Khan Jun 19 '22 at 15:47
  • Let me put it this way: a RecyclerView inside a fragment works fine when correctly handled. There's no magic but also nothing special; There's no "known" Android bug that I'm aware of, that causes the behavior you're describing. So I'd start by using the Android Debugger and/or looking at the Android source code (you can even set breakpoints in their code if you download the Sources from the SDK Manager) to see what part is not calling what. Alternatively, if you can make a small project that reproduces this and put it in online, maybe we can all see the bug you're talking about. – Martin Marconcini Jun 20 '22 at 07:59
  • Think about it, if you suspect there's a real Android Bug, Google is going to ask you for a sample project that exhibits the issue and where one is able to consistently reproduce it, so if you produce this now, we could take a look, and as a side-benefit you might find what (if that's the case) you're doing wrong, or what Android is doing wrong. Either way, you win. – Martin Marconcini Jun 20 '22 at 08:00
  • Yes ideally i should do that. But i'll need to buy some time. I did a lot of debugging. My recent findings are, even if I set adapter to null on getting new message, I still see old messages list, when app is launched 2nd time. On first app launch, this null clears the list on screen. So apparently, the reference to the view (defined in xml) is somehow lost.. – M. Usman Khan Jun 22 '22 at 17:42
  • Same result when i remove this RecyclerView from its parent. On second app launch the view is not removed. So its not the adapter, its the view reference. Actually my project has lots of flavors with lots of flavor specific files and all, so I dont think i'll be able to create a sample to show this bug. – M. Usman Khan Jun 22 '22 at 18:05
  • Even recreate() activity didn't clear the view :) . It refreshed the screen for first launch. – M. Usman Khan Jun 22 '22 at 19:07
0

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)
M. Usman Khan
  • 3,689
  • 1
  • 59
  • 69