I'm building a chat app and in the chats fragment I have currently one chat with one message in it. The problem is continuous call to onBindViewHolder
for single item. I found one related question here that says problem caused by itemview
's layoutparams But I did nothing with layoutparams of itemview
. Due to continuous call to onBindViewHolder
click listener for opening chat activity is not working either. check out the Logcat
detail:
2019-03-08 00:48:54.464 1672-1672/com.invogen.messagingapp E/ChatAdapter: Message Key = -L_OWO_jaN6ggbeoAlmI
2019-03-08 00:48:54.474 1672-1672/com.invogen.messagingapp E/ChatAdapter: ChatPath = https://messagingapp-db3e5.firebaseio.com/Chats/-L_OWO_fra0oDMflLMTE/messages
2019-03-08 00:48:54.480 1672-1672/com.invogen.messagingapp E/ChatAdapter: Message Key = -L_OWO_jaN6ggbeoAlmI
2019-03-08 00:48:54.490 1672-1672/com.invogen.messagingapp E/ChatAdapter: ChatPath = https://messagingapp-db3e5.firebaseio.com/Chats/-L_OWO_fra0oDMflLMTE/messages
2019-03-08 00:48:54.497 1672-1672/com.invogen.messagingapp E/ChatAdapter: Message Key = -L_OWO_jaN6ggbeoAlmI
2019-03-08 00:48:54.507 1672-1672/com.invogen.messagingapp E/ChatAdapter: ChatPath = https://messagingapp-db3e5.firebaseio.com/Chats/-L_OWO_fra0oDMflLMTE/messages
2019-03-08 00:48:54.515 1672-1672/com.invogen.messagingapp E/ChatAdapter: Message Key = -L_OWO_jaN6ggbeoAlmI
2019-03-08 00:48:54.524 1672-1672/com.invogen.messagingapp E/ChatAdapter: ChatPath = https://messagingapp-db3e5.firebaseio.com/Chats/-L_OWO_fra0oDMflLMTE/messages
2019-03-08 00:48:54.532 1672-1672/com.invogen.messagingapp E/ChatAdapter: Message Key = -L_OWO_jaN6ggbeoAlmI
2019-03-08 00:48:54.540 1672-1672/com.invogen.messagingapp E/ChatAdapter: ChatPath = https://messagingapp-db3e5.firebaseio.com/Chats/-L_OWO_fra0oDMflLMTE/messages
2019-03-08 00:48:54.552 1672-1672/com.invogen.messagingapp E/ChatAdapter: Message Key = -L_OWO_jaN6ggbeoAlmI
2019-03-08 00:48:54.558 1672-1672/com.invogen.messagingapp E/ChatAdapter: ChatPath = https://messagingapp-db3e5.firebaseio.com/Chats/-L_OWO_fra0oDMflLMTE/messages
2019-03-08 00:48:54.568 1672-1672/com.invogen.messagingapp E/ChatAdapter: Message Key = -L_OWO_jaN6ggbeoAlmI
2019-03-08 00:48:54.575 1672-1672/com.invogen.messagingapp E/ChatAdapter: ChatPath = https://messagingapp-db3e5.firebaseio.com/Chats/-L_OWO_fra0oDMflLMTE/messages
2019-03-08 00:48:54.583 1672-1672/com.invogen.messagingapp E/ChatAdapter: Message Key = -L_OWO_jaN6ggbeoAlmI
2019-03-08 00:48:54.591 1672-1672/com.invogen.messagingapp E/ChatAdapter: ChatPath = https://messagingapp-db3e5.firebaseio.com/Chats/-L_OWO_fra0oDMflLMTE/messages
2019-03-08 00:48:54.600 1672-1672/com.invogen.messagingapp E/ChatAdapter: Message Key = -L_OWO_jaN6ggbeoAlmI
2019-03-08 00:48:54.608 1672-1672/com.invogen.messagingapp E/ChatAdapter: ChatPath = https://messagingapp-db3e5.firebaseio.com/Chats/-L_OWO_fra0oDMflLMTE/messages
2019-03-08 00:48:54.616 1672-1672/com.invogen.messagingapp E/ChatAdapter: Message Key = -L_OWO_jaN6ggbeoAlmI
2019-03-08 00:48:54.624 1672-1672/com.invogen.messagingapp E/ChatAdapter: ChatPath = https://messagingapp-db3e5.firebaseio.com/Chats/-L_OWO_fra0oDMflLMTE/messages
2019-03-08 00:48:54.631 1672-1672/com.invogen.messagingapp E/ChatAdapter: Message Key = -L_OWO_jaN6ggbeoAlmI
2019-03-08 00:48:54.642 1672-1672/com.invogen.messagingapp E/ChatAdapter: ChatPath = https://messagingapp-db3e5.firebaseio.com/Chats/-L_OWO_fra0oDMflLMTE/messages
2019-03-08 00:48:54.649 1672-1672/com.invogen.messagingapp E/ChatAdapter: Message Key = -L_OWO_jaN6ggbeoAlmI
2019-03-08 00:48:54.658 1672-1672/com.invogen.messagingapp E/ChatAdapter: ChatPath = https://messagingapp-db3e5.firebaseio.com/Chats/-L_OWO_fra0oDMflLMTE/messages
2019-03-08 00:48:54.740 1672-1672/com.invogen.messagingapp E/ChatAdapter: Message Key = -L_OWO_jaN6ggbeoAlmI
This process do not stop until I exit the app. Here is my firebase database
structure for this chats node
Below is my chats fragment code which calls the ChatAdapter
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_chats, container, false);
this.mContext = view.getContext();
initViews(view);
mChatsDBReference = FirebaseDatabase.getInstance().getReference().child(AppConstants.CHATS_NODE);
fabCreate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Floating Action Button's Action code here for creating new chat group
}
});
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.setAdapter(new ChatAdapter(chatRoomChatsList, chatRoomKeysList));
mChatsDBReference.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
chatRoomChatsList.clear();
chatRoomKeysList.clear();
if (dataSnapshot.exists()) {
Log.e(TAG, "Snapshot Exits\nValue = " + dataSnapshot.toString());
for (DataSnapshot ds : dataSnapshot.getChildren()) {
Chats chat = ds.getValue(Chats.class);
Log.e(TAG, "Chat Key = " + ds.getKey() + "\n DataSnapshot = " + ds.toString());
chatRoomChatsList.add(chat);
chatRoomKeysList.add(ds.getKey());
}
}
mRecyclerView.getAdapter().notifyDataSetChanged();
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
return view;
}
Here is my ChatAdapter
class
public class ChatAdapter extends RecyclerView.Adapter<ChatViewHolder> {
private String TAG = "ChatAdapter";
private List<Chats> chatRoomChatsList;
private Context mContext;
// private String pushKey;
private List<String> chatRoomChatsKeyList;
private List<FriendlyMessage> messageList;
public ChatAdapter(List<Chats> chatList, List<String> chatRoomChatsKeyList) {
this.chatRoomChatsList = chatList;
// this.pushKey = pushKey;
this.chatRoomChatsKeyList = chatRoomChatsKeyList;
}
@NonNull
@Override
public ChatViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int itemViewType) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.chats_layout,
viewGroup, false);
Log.e(TAG, "onCreateView LayoutId = " + itemViewType + " Inside MyLayout");
return new ChatViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull final ChatViewHolder holder, int position) {
mContext = holder.chatTitleTV.getContext();
int pos = holder.getAdapterPosition();
setupLastMessageData(holder);
Chats chat = chatRoomChatsList.get(pos);
String chatTitle = chat.getChatName();
holder.chatTitleTV.setText(chatTitle);
holder.avatarTV.setText(chatTitle);
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String pushkey = chatRoomChatsKeyList.get(holder.getAdapterPosition());
Log.e(TAG, "pushKey = " + pushkey);
Bundle bundle = new Bundle();
bundle.putString("pushKey", pushkey);
Intent intent = new Intent(mContext, ChatMessagesActivity.class);
intent.putExtras(bundle);
mContext.startActivity(intent);
}
});
}
private void setupLastMessageData(final ChatViewHolder holder) {
DatabaseReference mSingleChatReference =
FirebaseDatabase.getInstance().getReference()
.child(AppConstants.CHATS_NODE)
.child(chatRoomChatsKeyList.get(holder.getAdapterPosition()))
.child("messages");
Log.e(TAG, "ChatPath = " + mSingleChatReference);
mSingleChatReference.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
messageList = new ArrayList<>();
if (dataSnapshot.exists()) {
for (DataSnapshot ds : dataSnapshot.getChildren()) {
FriendlyMessage msg = ds.getValue(FriendlyMessage.class);
Log.e(TAG, "Message Key = " + ds.getKey());
messageList.add(msg);
}
}
holder.lastMessageTV.setText(messageList.get(messageList.size() - 1).getMsgText());
holder.timeTV.setText(messageList.get(messageList.size() - 1).getMsgDate());
notifyDataSetChanged();
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
}
@Override
public int getItemCount() {
return chatRoomChatsList.size();
}
}
If any other information is need let me know. Any help will be appreciated.
In your createView() you're calling notifyDataSetChanged() which causes the RecyclerView to rebind and update all of it's contents. Part of that process includes calls to adapter.onBindViewHolder() to bind the data set to the individual item views that make up the list. But inside adapter.onBindViewHolder() you have a call to setupLastMessageData() which internally again calls notifyDataSetChanged() which begins the cycle again. This creates an infinite loop of notify->bind->notify->bind
My suggestion is have those firebase listeners outside of your adapter, listen for the necessary changes and updates for your data structure and pass those changes on to the adapter with a single call to notifyDataSetChanged(). You can even make it more efficient by not calling notifyDataSetChanged() and just call the more granular notifyXXX methods() from the adapter if you are able to determine which messages were actually added or changed.