1

I am developing an Android chat app with Paging library 3 androidx.paging:paging-runtime-ktx:3.0.0-alpha12

The paging library retrieves chat messages from the Room database.

When I insert a new chat message to the ChatMessage table in the Room database, while scrolling recycler view of chat message, it throws an exception as per GIF below. The send button just inserts a new item into the ChatMessage table.

enter image description here

This is the error logs.

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.beeswork.balance, PID: 24171
    java.lang.UnsupportedOperationException: Stable ids are unsupported on PagingDataAdapter.
        at androidx.paging.PagingDataAdapter.setHasStableIds(PagingDataAdapter.kt:127)
        at com.beeswork.balance.ui.chat.ChatFragment.setupChatRecyclerView(ChatFragment.kt:71)
        at com.beeswork.balance.ui.chat.ChatFragment.access$setupChatRecyclerView(ChatFragment.kt:24)
        at com.beeswork.balance.ui.chat.ChatFragment$bindUI$1.invokeSuspend(ChatFragment.kt:58)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at android.os.Handler.handleCallback(Handler.java:751)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6077)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
Disconnected from the target VM, address: 'localhost', transport: 'socket'

This is the code to setup the chat recycler view in Chat fragment

private fun setupChatRecyclerView() {
    chatPagingAdapter = ChatPagingAdapter()
    rvChat.adapter = chatPagingAdapter
    val layoutManager = LinearLayoutManager(this@ChatFragment.context)
    layoutManager.orientation = LinearLayoutManager.VERTICAL
    layoutManager.reverseLayout = true
    rvChat.layoutManager = layoutManager
    lifecycleScope.launch {
        viewModel.chatMessages.collectLatest {
            chatPagingAdapter.submitData(it)
        }
    }
}

This is the chatMessages in ChatViewModel

val chatMessages = Pager(
    PagingConfig(
        pageSize = 30,
        enablePlaceholders = false,
        maxSize = 150
    )
) {
    balanceRepository.getChatMessages(chatId)
}.flow.cachedIn(viewModelScope)

This is the query in ChatDAO

@Query("select * from chatMessage where chatId = :chatId order by case when id is null then 0 else 1 end, id desc, messageId desc")
fun getChatMessages(chatId: Long): PagingSource<Int, ChatMessage>

This is the ChatPagingAdapter extends PagingDataAdapter

class ChatPagingAdapter : PagingDataAdapter<ChatMessage, ChatPagingAdapter.MessageViewHolder>(
    diffCallback
) {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MessageViewHolder {
        return when (viewType) {
            ChatMessage.Status.RECEIVED.ordinal -> MessageViewHolder(parent.inflate(R.layout.item_chat_message_received))
            ChatMessage.Status.SENT.ordinal -> MessageViewHolder(parent.inflate(R.layout.item_chat_message_sent))
            else -> MessageViewHolder(parent.inflate(R.layout.item_chat_message_received))
        }
    }

    override fun onBindViewHolder(holder: MessageViewHolder, position: Int) {
        getItem(position)?.let {
            when (holder.itemViewType) {
                ChatMessage.Status.RECEIVED.ordinal -> holder.bindMessageReceived(it)
                ChatMessage.Status.SENT.ordinal -> holder.bindMessageSent(it)
            }
        }
    }

    override fun getItemViewType(position: Int): Int {
        return getItem(position)?.let {
            return if (it.status == ChatMessage.Status.RECEIVED) ChatMessage.Status.RECEIVED.ordinal else ChatMessage.Status.SENT.ordinal
        } ?: kotlin.run {
            return ChatMessage.Status.RECEIVED.ordinal
        }
    }

    companion object {
        private val diffCallback = object : DiffUtil.ItemCallback<ChatMessage>() {
            override fun areItemsTheSame(oldItem: ChatMessage, newItem: ChatMessage): Boolean =
                oldItem.id == newItem.id

            override fun areContentsTheSame(oldItem: ChatMessage, newItem: ChatMessage): Boolean =
                oldItem == newItem
        }
    }

    class MessageViewHolder(
        itemView: View
    ) : RecyclerView.ViewHolder(itemView) {
        fun bind {...}
    }
   }

Here are questions

  1. Can I keep my scrolling while inserting a new item to chat message table please?
  2. If not possible, then how can I resolve the exception please?
kisung Tae
  • 217
  • 5
  • 19

0 Answers0