Home > Software design >  How to prevent racing condition in Android RecyclerView's Adapter?
How to prevent racing condition in Android RecyclerView's Adapter?

Time:09-21

I have a classic implementation of a recycler view that, when I click on an item inside the recycler view, that item gets deleted.

The problem is that, when I successively click twice one after another (without any noticeable delay between the clicks) on an item in that recycler view, then the second click on that same item is registered at a different position.

The way I identify the item that received the click is by holder.adapterPosition (where holder is an instantiation of ViewHolder class). I wonder if I'm doing wrong by relying on this.

To further troubleshoot, I added the following println statement to troubleshoot:

println("layoutpos ${holder.layoutPosition} adapterpos ${holder.adapterPosition} oldpos ${holder.oldPosition}")

Then, as I repeated those successive clicks, I got the following output in Android studio's Run tab:

[Galaxy_Nexus_API_22 [emulator-5554]]: I/System.out: layoutpos 1 adapterpos 1 oldpos -1
[Galaxy_Nexus_API_22 [emulator-5554]]: I/System.out: layoutpos 0 adapterpos -1 oldpos -1

Right now, my thoughts are: use adapterPosition, and ignore it when its value is -1 (assume that -1 means a declaration of a racing condition). But I feel that I might be missing something deeper.

How to handle this situation?

CodePudding user response:

Show the user that the system is refreshing while you're disabling the user from deleting a new object until the previous transaction is completed.

CodePudding user response:

I found two solutions:

if (holder.adapterPosition == -1) return // racing condition; do nothing
// else, do stuff

This does the trick. However, it is not elegant in my view, as: why receive clicking events to begin with if we are not supposed to? It doesn't seem to be solving the problem from its roots.

To solve it more elegantly (from its roots), I did this in the setOnClickListener:

holder.item.setOnClickListener {
    // we don't want 2nd click right?  so let's delete the listener
    holder.item.setOnClickListener{}

    /* then, do the stuff for this listener.  this stuff 
       will be done once, as we deleted its listener earlier,
       so, subsequent clicks are not possible. */
}

This way, the item with that listener is clicked on once, and a 2nd click does not happen to begin with, hence no racing condition is possible from its roots. Because the clicking listener is deleted right when the 1st click is received. Should I want to allow the item to get clicks again, I can re-define a listener for it again.

  • Related