问题
Today, when I was working on my android project, I saw this weird behavior of my app. When I click on an icon and move to a different activity, which would basically insert another child into the node to which first activity was listening to. But to my surprise, when return back, everything in the screen was doubled. When I refresh the view, it again would become normal. I repeated it many times, thinking it may be a glitch. But same result every time. When I look into database, new child was never created.
This is my code for listener. (doOnDataChange is an extension function, I defined for onValueEventListener)
val chats = mutableListOf<Chat>()
chatsListener = dbChatsReference.orderByChild(NODE_LAST_MESSAGE_TIME).doOnDataChange { it ->
chats.clear()
viewLifecycleOwner.lifecycleScope.launch(IO) {
// some IO work
for (ss in it.children) {
Log.d(TAG, "onDataChange: ${ss.key!!} , ${Thread.currentThread()}")
// populate chats with appropriate data
}
}
}
Then I logged what's happening.
2020-12-08 23:52:39.721 4114-4200/com.skb.skara D/ChatsFragment: onDataChange: cleared
2020-12-08 23:52:40.043 4114-4200/com.skb.skara D/ChatsFragment: onDataChange: cleared
2020-12-08 23:52:40.132 4114-4200/com.skb.skara D/ChatsFragment: onDataChange: j7ZmBA650WVmvd9z6BsN7UsvPlJ2VtDh2KtpeDhwXN386b580Lpr6dj2 , Thread[DefaultDispatcher-worker-2,5,main]
2020-12-08 23:52:40.133 4114-4200/com.skb.skara D/ChatsFragment: onDataChange: lIq43hHgzKfsE0zbYIeiH4dhswT2KQTXik3LjYMriCOdPoTKLNAtoVf2 , Thread[DefaultDispatcher-worker-2,5,main]
2020-12-08 23:52:40.139 4114-4200/com.skb.skara D/ChatsFragment: onDataChange: lIq43hHgzKfsE0zbYIeiH4dhswT2MWUYn8kW4MPlZvnn420XrN4p5kq2 , Thread[DefaultDispatcher-worker-2,5,main]
2020-12-08 23:52:40.140 4114-4201/com.skb.skara D/ChatsFragment: onDataChange: j7ZmBA650WVmvd9z6BsN7UsvPlJ2VtDh2KtpeDhwXN386b580Lpr6dj2 , Thread[DefaultDispatcher-worker-3,5,main]
2020-12-08 23:52:40.141 4114-4201/com.skb.skara D/ChatsFragment: onDataChange: lIq43hHgzKfsE0zbYIeiH4dhswT2KQTXik3LjYMriCOdPoTKLNAtoVf2 , Thread[DefaultDispatcher-worker-3,5,main]
2020-12-08 23:52:40.143 4114-4200/com.skb.skara D/ChatsFragment: onDataChange: lIq43hHgzKfsE0zbYIeiH4dhswT2VtDh2KtpeDhwXN386b580Lpr6dj2 , Thread[DefaultDispatcher-worker-2,5,main]
2020-12-08 23:52:40.145 4114-4201/com.skb.skara D/ChatsFragment: onDataChange: lIq43hHgzKfsE0zbYIeiH4dhswT2MWUYn8kW4MPlZvnn420XrN4p5kq2 , Thread[DefaultDispatcher-worker-3,5,main]
2020-12-08 23:52:40.146 4114-4200/com.skb.skara D/ChatsFragment: onDataChange: lIq43hHgzKfsE0zbYIeiH4dhswT2j7ZmBA650WVmvd9z6BsN7UsvPlJ2 , Thread[DefaultDispatcher-worker-2,5,main]
2020-12-08 23:52:40.148 4114-4201/com.skb.skara D/ChatsFragment: onDataChange: lIq43hHgzKfsE0zbYIeiH4dhswT2VtDh2KtpeDhwXN386b580Lpr6dj2 , Thread[DefaultDispatcher-worker-3,5,main]
2020-12-08 23:52:40.150 4114-4200/com.skb.skara D/ChatsFragment: onDataChange: -MO2_oyUjEV55HAGlzXN , Thread[DefaultDispatcher-worker-2,5,main]
2020-12-08 23:52:40.152 4114-4201/com.skb.skara D/ChatsFragment: onDataChange: lIq43hHgzKfsE0zbYIeiH4dhswT2j7ZmBA650WVmvd9z6BsN7UsvPlJ2 , Thread[DefaultDispatcher-worker-3,5,main]
2020-12-08 23:52:45.395 4114-4114/com.skb.skara D/ChatsFragment: onCreateView:
2020-12-08 23:52:45.842 4114-4200/com.skb.skara D/ChatsFragment: onDataChange: cleared
2020-12-08 23:52:46.263 4114-4200/com.skb.skara D/ChatsFragment: onDataChange: j7ZmBA650WVmvd9z6BsN7UsvPlJ2VtDh2KtpeDhwXN386b580Lpr6dj2 , Thread[DefaultDispatcher-worker-2,5,main]
2020-12-08 23:52:46.264 4114-4200/com.skb.skara D/ChatsFragment: onDataChange: lIq43hHgzKfsE0zbYIeiH4dhswT2KQTXik3LjYMriCOdPoTKLNAtoVf2 , Thread[DefaultDispatcher-worker-2,5,main]
2020-12-08 23:52:46.268 4114-4200/com.skb.skara D/ChatsFragment: onDataChange: lIq43hHgzKfsE0zbYIeiH4dhswT2MWUYn8kW4MPlZvnn420XrN4p5kq2 , Thread[DefaultDispatcher-worker-2,5,main]
2020-12-08 23:52:46.273 4114-4200/com.skb.skara D/ChatsFragment: onDataChange: lIq43hHgzKfsE0zbYIeiH4dhswT2VtDh2KtpeDhwXN386b580Lpr6dj2 , Thread[DefaultDispatcher-worker-2,5,main]
2020-12-08 23:52:46.278 4114-4200/com.skb.skara D/ChatsFragment: onDataChange: lIq43hHgzKfsE0zbYIeiH4dhswT2j7ZmBA650WVmvd9z6BsN7UsvPlJ2 , Thread[DefaultDispatcher-worker-2,5,main]
Then I remembered, as mentioned in this blog, when you make some changes to database, immediately listeners are triggered, without actually waiting for the write to complete. Afterwards, if the write fails, the listeners are triggered again with the old values to nullify the previous trigger. Since I have used coroutines here, chats list is being updated simultaneously by two threads. This can happen even when two changes are made to the node with very short delay in between.
So can someone tell me how to overcome this race condition.
回答1:
Avoid using mutable lists and data classes, but rather just use map
on your data to transform it to a read-only List of the type you need for your list view.
Make your RecyclerView.Adapter store a var
property so it is changed all at once. Change the data list and call notifyDataSetChanged()
. Then it is only ever looking at one list at a time.
来源:https://stackoverflow.com/questions/65205301/how-to-handle-race-conditions-in-kotlin-when-listening-to-a-firebase-database-n