java.util.ConcurrentModificationException in android studio? - android

I want to create a chat app with android studio and when I want to display users in my app, app crashed and my code is below:
private void readChats()
{
mUsers = new ArrayList<>();
reference = FirebaseDatabase.getInstance().getReference("Users");
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot)
{
mUsers.clear();
for (DataSnapshot snapshot : dataSnapshot.getChildren())
{
User user = snapshot.getValue(User.class);
for (String id : userList){
assert user != null;
if (user.getId().equals(id)) {
if (mUsers.size() != 0) {
for (User user1 : mUsers) {
if (!user.getId().equals(user1.getId())) {
mUsers.add(user);
}
}
}else {
mUsers.add(user);
}
}
}
}
userAdapter = new UserAdapter(getContext(), mUsers);
recyclerView.setAdapter(userAdapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError)
{
}
});
}
and my error is below:
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.next(ArrayList.java:831)
at com.example.mahdi.chatapp.Fragments.ChatsFragment$2.onDataChange(ChatsFragment.java:101)
and error from this line:
for (User user1 : mUsers)
I can't fix this error please help me:

You can't change this list ("mUsers") in loop, because loop use count items for limits.
If you make temp variable, or use this code:
for (User user : new ArrayList< User >(mUsers)) {
if (!user.getId().equals(user1.getId())) {
}
}
I hope it helpful

If you use plain for loop instead of enhanced for loop, your problem is resolved.

if (mUsers.size() != 0) {
for (User user1 : mUsers) {
if (!user.getId().equals(user1.getId())) {
mUsers.add(user);
}
}
}else {
mUsers.add(user);
}
Aside from the ConcurrentModificationException (which comes from the mUsers.add(user) inside the loop over mUsers), I don't think this is the logic you intend. This would add user to the list N times, where N is the number of users in the list with differing IDs.
I suspect you might want something like:
if (mUsers.stream().noneMatch(u -> user.getId().equals(u.getId())) {
mUsers.add(user);
}
which adds user once, if no other user with that ID is present.
You might also consider using a Map<String, User>, where the key is the user's ID. Then you could use:
map.computeIfAbsent(user.getId(), k -> user);

I was too facing this issue.
Better you can use the below code snippet which will make sure that only one user has been added in the ChatFragment.
reference=FirebaseDatabase.getInstance().getReference("Users");
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
mUsers.clear();
for(DataSnapshot snapshot:dataSnapshot.getChildren()){
User user=snapshot.getValue(User.class);
//Display 1 user from chat
for (String id:usersList){
assert user != null;
if(user.getId().equals(id)){
if(mUsers.size()!=0){
int flag=0;
for(User u : mUsers) {
if (user.getId().equals(u.getId())) {
flag = 1;
break;
}
}
if(flag==0)
mUsers.add(user);
}else{
mUsers.add(user);
}
}
}
}
userAdapter=new UserAdapter(getContext(),mUsers,true);
recyclerView.setAdapter(userAdapter);
Thank You

Related

Android Firebase Database combine multi queries

I want show a list of users (using RecyclerView , adapter ...) , the list contain user's info (photo , name , email ....) and also a count of the comments about this user , for the user's info I made a firebase query and I get all the info as I want
al = new ArrayList<>();
FirebaseDatabase.getInstance().getReference().child("Seeker").child(categorie).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// clear data before
al.clear();
for (DataSnapshot dataLoop : dataSnapshot.getChildren()) {
String getcity = dataLoop.child("City").getValue().toString().trim();
if (getcity.equalsIgnoreCase(City.trim())) {
p = new Person();
p.setName(dataLoop.child("Name").getValue(String.class));
p.setId(dataLoop.child("Id").getValue(String.class));
if (dataLoop.hasChild("urlToImage")) {
p.setUrl(dataLoop.child("urlToImage").getValue(String.class));
} else {
p.setImage(R.drawable.profile);
}
readData(new MyCallback() {
#Override
public void onCallback(String value) {
Log.d("TAG", value);
p.setComms(value);
}
}, dataLoop.child("Id").getValue(String.class));
al.add(p);
}
}
md = new MyAdaptor(MyClass.this, al);
rv.setAdapter(md);
}
...
...
public void readData(final MyCallback myCallback, String value) {
database.child("Review").child(value).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot dataLoop : dataSnapshot.getChildren()) {
count = dataLoop.getChildrenCount();
Log.d("TAG", "count = " + count);
p.setComms(String.valueOf(count));
}
myCallback.onCallback(String.valueOf(count));
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
}
public interface MyCallback {
void onCallback(String value);
}
my problem is that I want add one more info for each user , it's the count of review for each one , I tried to make another query inside the loop but as firebase calls are asynchronous
it's not working , the layout doesn't show numbers of reviews.
here is a sample of my firebase database structure
-Review
-HDGFDGDGDB (user id)
-FSETTDGDG (user who made the review)
-1 : review1
-2 : review2
-Seeker
-HDGFDGDGDB (user id)
-city : "NY"
-name : "Jhon"......
what's the best solution to combine the two queries in order to show all the info in the recyclerView.

This code is showing me an error when i am trying to call user1 from recycler view

When i was trying to call user1 from recyclerview it crashes i was trying to solve it for hours now but it still if not working so if you can help me i would appriciate it
private void readChats() {
users = new ArrayList<>();
reference = FirebaseDatabase.getInstance().getReference("teacher");
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
users.clear();
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
User user = snapshot.getValue(User.class);
for (String id : usersList) {
if (user.getId().equals(id)) {
if (users.size() != 0) {
for (User user1 : users) {
if (!user.getId().equals(user1.getId())) {
users.add(user);
}
}
}else {
users.add(user);
}
}
}
}
userAdapter = new UserAdapter(getContext(), users);
recyclerView.setAdapter(userAdapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
log cat is showing me error on this code for (User user1 : users)
thanks in advance

I am getting ConcurrentModificationException while creating a chat application

While displaying the recent chats fragment in the application I am fetching my chats from firebase and filtering out the chats by their receiverID and senderID in the chat object to display the recent chats.
The problem says ConcurrentModificationException in ArrayList and it looks like due to the complexity of searching id in the array it occurred, I need a solution to minimize this chat filteration complexity.
// private List<String> stringList; Declaration at top
stringList = new ArrayList<>();
firebaseUser = FirebaseAuth.getInstance().getCurrentUser();
databaseReference = FirebaseDatabase.getInstance().getReference("BaatCheet/Chats/");
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
//userModelList.clear();
for (DataSnapshot dataSnapshot1 : dataSnapshot.getChildren()) {
MessageModel messageModel = dataSnapshot1.getValue(MessageModel.class);
if (messageModel.getSender().equals(firebaseUser.getUid())){
stringList.add(messageModel.getReceiver());
}
if (messageModel.getReceiver().equals(firebaseUser.getUid())){
stringList.add(messageModel.getSender());
}
}
readChat();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
The read chat function
private void readChat() {
userModelList = new ArrayList<>();
databaseReference = FirebaseDatabase.getInstance().getReference("BaatCheet/Users/");
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
userModelList.clear();
for (DataSnapshot dataSnapshot1 : dataSnapshot.getChildren()){
UserModel userModel = dataSnapshot1.getValue(UserModel.class);
for (String id: stringList){
if (userModel.getId().equals(id)){
if (userModelList.size() !=0){
for (UserModel userModel1 : userModelList){
if (!userModel.getId().equals(userModel1.getId())){
userModelList.add(userModel);
Log.d("DataAdded",userModel.getId());
} // If the existing list don't have same value for sender and reciever
} // end of inner userModel
} else {
userModelList.add(userModel);
Log.d("DataAdded",userModel.getId());
} // end of else
} // end of userModel id equals string id
} // end of String is loop
} // end of DataSnapshot loop
usersAdapter = new UsersAdapter(userModelList);
recyclerView.setAdapter(usersAdapter);
} // end of onDataChange
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}// end of readChat()
The results would be the recyclerView of recent chats containing the chats which contain messages either send by the sender or receiver to each other.
In the following snippet of code :
for (UserModel userModel1 : userModelList){
if (!userModel.getId().equals(userModel1.getId())){
userModelList.add(userModel);
Log.d("DataAdded",userModel.getId());
} // If the existing list don't have same value for sender and reciever
} //
You are modifying userModelList while iterating through userModelList. This is not allowed and is the cause of ConcurrentModificationException.
There are few ways to simplify the logic, the simplest (albeit not the best) would be to convert this foreach loop into a simple for i loop.
for (int i = 0; i< userModelList.size(); i++) {
UserModel userModel1 = userModelList.get(i);
if (!userModel.getId().equals(userModel1.getId())){
userModelList.add(userModel);
Log.d("DataAdded",userModel.getId());
} // If the existing list don't have same value for sender and reciever
} //
Basically to handle this complexity I am now using separate nodes just for storing the chatLists as such now I don't need to read chats for filtering the recent chats.
The below code is for creating a new node everytime user sends a message it will update the node if the recieverID is different.
dbrefChatList = FirebaseDatabase.getInstance().
getReference("BaatCheet/ChatList/")
.child(senderuserID)
.child(receiveruserID);
dbrefChatList.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (!dataSnapshot.exists()){
dbrefChatList.child("id").setValue(receiveruserID);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
As such ChatList is a Model Class which contains single String called "id" and this id will be used to search in the node.
The below code is for the ChatFragment which fetches the chatList from firebase and set the data to recycler view.
// private List<ChatList> chatList; Declaration at top
chatListList = new ArrayList<>();
firebaseUser = FirebaseAuth.getInstance().getCurrentUser();
databaseReference = FirebaseDatabase
.getInstance()
.getReference("BaatCheet/ChatList")
.child(firebaseUser.getUid());
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
chatListList.clear();
for (DataSnapshot snapshot : dataSnapshot.getChildren()){
ChatList chatList = snapshot.getValue(ChatList.class);
chatListList.add(chatList);
}
myChatList();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
The function myChatList act as the function of readChat in the problem statement.
private void myChatList() {
userModelList = new ArrayList<>();
databaseReference = FirebaseDatabase.getInstance().getReference("BaatCheet/Users/");
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
userModelList.clear();
for (DataSnapshot snapshot : dataSnapshot.getChildren()){
UserModel userModel = snapshot.getValue(UserModel.class);
for (ChatList chatList : chatListList){
if (userModel.getId().equals(chatList.getId())){
userModelList.add(userModel);
}
}
}
usersAdapter = new UsersAdapter(userModelList);
recyclerView.setAdapter(usersAdapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
You can set a flag while traversing the array, after it's done, add it if it doesn't exist yet:
// Display 1 user from chats
for (String id : str_usersList) {
if (user.getId().equals(id)) {
if (userList.size() != 0) {
boolean exists = false;
// If not exists then add
for (User user1 : userList) {
if (user.getId().equals(user1.getId())) {
exists = true;
}
}
if (!exists) {
userList.add(user);
}
} else {
userList.add(user);
}
}
}

ValueEventListener never being entered

I have a ValueEventListener, and the inner code is never being reached. My app is authenticating, but I can't retrieve data and I'm not sure what I'm missing. In the code below, when I check, uid has the correct value, and the data path ("Users/[uid]") is correct, but none of the inner code in the value event listener is never being reached. I've also tried to place a listener right at the root, with the same result.
I've set up Firebase using the menu options, adding both Authentication and Realtime Database to my app; authentication is working fine, so I'm wondering if there is something else that needs to be configured for the realtime database? I've read the documentation and walked through many examples but I can't see anything I'm missing.
uid = FirebaseAuth.getInstance().getCurrentUser().getUid().toString();
DatabaseReference userRef = ref.child("Users").child(uid).getRef();
ValueEventListener userListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
if (user == null) {
SettingsLoad();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e("User", "User not found" );
}
};
userRef.addListenerForSingleValueEvent(userListener);
Try this!
I've just added else condition and log or toast the results!!
uid = FirebaseAuth.getInstance().getCurrentUser().getUid().toString();
DatabaseReference userRef = ref.child("Users").child(uid).getRef();
ValueEventListener userListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
if (user == null) {
SettingsLoad();
}
**else {
Log.d("tag","User Found!");
}**
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e("User", "User not found" );
}
};
userRef.addListenerForSingleValueEvent(userListener);

How to get a list of nodes by their key

I'm developing an application for Android that uses firebase.
The application has Users and each user has Friends.
users: {
one: {
name: Mark,
friends: {
two: true,
three: true
},
two: {
name: Carl
},
three: {
name: Gustav
}
}
In this example, Mark has two friends (Carl and Gustav), the other two don't have friends.
I want to get a Mark's Friends List.
String userId = "one";
DatabaseReference friendsDb = db.getReference("users").child(userId).child("friends");
final DatabaseReference usersDb = db.getReference("users");
ValueEventListener friendsListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
GenericTypeIndicator<LinkedHashMap<String,Boolean>> t = new GenericTypeIndicator<LinkedHashMap<String,Boolean>>() {};
LinkedHashMap<String,Boolean> tDataset = dataSnapshot.getValue(t);
users.clear();
for( String userId : tDataset.keySet() ) {
usersDb.child(userId).addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
// how to return the user???
// users.add(user);
}
});
}
mAdapter.notifyDataSetChanged();
}
};
friendsDb.addValueEventListener(friendsListener);
Am I using a correct approach about data modeling and indexes?
How is it supposed to give me back the user list that I need?
I understand that listening to a resource it is an async operation, is there a way to get the values that I need in one shot?
Any help will be appreciated! Thanks!
EDIT
Solution proposed by Frank van Puffelen it's correct in the concept but it's not correctly implemented. The concept is to call the mAdapter.notifyDataSetChanged(); when all the children has been retrieved from firebase db. But it have to check the dimension of the first snapshot, intead of the second, as below.
DatabaseReference friendsDb = db.getReference("users").child(userId).child("friends");
final DatabaseReference usersDb = db.getReference("users");
ValueEventListener friendsListener = new ValueEventListener() {
#Override
public void onDataChange(final DataSnapshot dataSnapshot) {
GenericTypeIndicator<HashMap<String,Boolean>> t = new GenericTypeIndicator<HashMap<String,Boolean>>() {};
HashMap<String,Boolean> tDataset = dataSnapshot.getValue(t);
final int usersSize = tDataset.size();
users.clear();
for( String userId : tDataset.keySet() ) {
usersDb.child(userId).addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
users.add(user);
if (users.size() == usersSize) {
mAdapter.notifyDataSetChanged();
}
}
});
}
}
};
friendsDb.orderByKey().addValueEventListener(friendsListener);
There is no Firebase equivalent of SQLs WHERE id IN (1,2,3). Performance-wise that is not needed, since Firebase pipelines the requests.
You code looks fine to me, except that you're not adding the user to the list. I expect that you're having trouble defining the "exit condition" for that loop, which is:
ValueEventListener friendsListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
GenericTypeIndicator<LinkedHashMap<String,Boolean>> t = new GenericTypeIndicator<LinkedHashMap<String,Boolean>>() {};
LinkedHashMap<String,Boolean> tDataset = dataSnapshot.getValue(t);
users.clear();
for( String userId : tDataset.keySet() ) {
usersDb.child(userId).addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
users.add(user);
if (users.size() == dataSnapshot.getChildrenCount()) {
mAdapter.notifyDataSetChanged();
}
}
});
}
}
};
Try arranging your data this way:
users: {
one: {
name: Mark
},
two: {
name: Carl
},
three: {
name: Gustav
}
},
friends : {
Mark : {
two : true,
three : true
}
}

Categories

Resources