infinite loop in onDataChange(DataSnapshot dataSnapshot) fn in firebase - android

I have a code something like this:
change_status_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mfirebasedatabase.getReference().child("genre_items").child(genre_status_selected).child(genreitem_status_selected).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(genre_status_selected!="" && genreitem_status_selected!="" && dataSnapshot.child("status").getValue().equals("available"))
{
mfirebasedatabase.getReference().child("genre_items").child(genre_status_selected).child(genreitem_status_selected).child("status").setValue("unavailable");
}
else if(genre_status_selected!="" && genreitem_status_selected!="" && dataSnapshot.child("status").getValue().equals("unavailable"))
{
mfirebasedatabase.getReference().child("genre_items").child(genre_status_selected).child(genreitem_status_selected).child("status").setValue("available");
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
});
whenever I click the change status button , it changes the status ,but since it is in the onDataChange it leads to an infinite loop...changing its state infinite no of times...how to get rid of this? how to get out of this loop? I want to change its status once only.

change
addValueEventListener
to this
addListenerForSingleValueEvent
mfirebasedatabase.getReference().child("genre_items").child(genre_status_selected).child(genreitem_status_selected).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(genre_status_selected!="" && genreitem_status_selected!="" && dataSnapshot.child("status").getValue().equals("available"))
{
mfirebasedatabase.getReference().child("genre_items").child(genre_status_selected).child(genreitem_status_selected).child("status").setValue("unavailable");
}
else if(genre_status_selected!="" && genreitem_status_selected!="" && dataSnapshot.child("status").getValue().equals("unavailable"))
{
mfirebasedatabase.getReference().child("genre_items").child(genre_status_selected).child(genreitem_status_selected).child("status").setValue("available");
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});

Use addListenerForSingleValueEvent()
example
database.getReference().child("chats").child(senderRoom).child("messages").addListenerForSingleValueEvent(new ValueEventListener() {
#SuppressLint("NotifyDataSetChanged")
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
database.getReference("chats/"+senderRoom+"/messages").removeValue();
Toast.makeText(ChattingActivity.this, "All chats delete", Toast.LENGTH_LONG).show();
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
Toast.makeText(ChattingActivity.this, "Error! Chats not delete", Toast.LENGTH_LONG).show();
}
});

Related

ArrayList size remains zero despite adding items from Firebase

I have the following code that is supposed to read data from Firebase and add it to array lists namesList and addressList. I logged the values on onDataChange events. The logcat shows that data is infact read but when I iterate the list, it is of size zero. Can anyone please help me regarding this:
#Override
public void onStart() {
super.onStart();
activeOrders = FirebaseDatabase.getInstance().getReference().child("Ordered Items");
activeOrders.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
for (DataSnapshot child: snapshot.getChildren())
{
key = child.getKey().toString();
name = FirebaseDatabase.getInstance().getReference().child("Users").child(key).child("name");
address = FirebaseDatabase.getInstance().getReference().child("Users").child(key).child("shippingAddr");
name.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
Log.i("Username", snapshot.getValue().toString());
namesList.add(snapshot.getValue().toString());
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
address.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
Log.i("Address", snapshot.getValue().toString());
addressList.add(snapshot.getValue().toString());
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
Toast.makeText(this, "Size:" + namesList.size(), Toast.LENGTH_SHORT).show();
for(int i=0; i<namesList.size(); i++){
Toast.makeText(this, namesList.get(i).toString(), Toast.LENGTH_SHORT).show();
}
logout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
signOutUser();
}
});
You are iterating the list in the main thread, while the actual list takes some time to come back from firebase, that is because all firebase callbacks runs in background thread inclusively
So, the below code runs in main thread before the actual data comes in
for(int i=0; i<namesList.size(); i++){
Toast.makeText(this, namesList.get(i).toString(), Toast.LENGTH_SHORT).show();
}
And the below code runs in firebase background thread
public void onDataChange(#NonNull DataSnapshot snapshot) {
Log.i("Username", snapshot.getValue().toString());
namesList.add(snapshot.getValue().toString());
}
To solve this you can transfer the iteration as below
activeOrders.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
for (DataSnapshot child: snapshot.getChildren())
{
key = child.getKey().toString();
name = FirebaseDatabase.getInstance().getReference().child("Users").child(key).child("name");
address = FirebaseDatabase.getInstance().getReference().child("Users").child(key).child("shippingAddr");
name.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
Log.i("Username", snapshot.getValue().toString());
namesList.add(snapshot.getValue().toString());
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
address.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
Log.i("Address", snapshot.getValue().toString());
addressList.add(snapshot.getValue().toString());
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
for(int i=0; i<namesList.size(); i++){
Toast.makeText(this, namesList.get(i).toString(), Toast.LENGTH_SHORT).show();
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});

Control Button Visibility From Firebase

This Is My Code
#Override
protected void onStart() {
super.onStart();
control.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
s2=dataSnapshot.getValue(String.class);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
if (s1==s2){
button.setVisibility(View.INVISIBLE);
}
}
}
when trying to control visibility from Firebase above code does not work. If I try same without use of Firebase it works.
Would appreciate any advice
Actually addValueEventListener() is an ASYNC call, so before the value of s2 gets assigned it's being compared in your if(s1==s2) statement & the value of s2 is default or null while comparison.
Instead, you can try hiding your button inside your async call itself as
#Override
protected void onStart() {
super.onStart();
control.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
s2=dataSnapshot.getValue(String.class);
if ( s1.equalsIgnoreCase(s2) )
button.setVisibility(View.INVISIBLE);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {}
});
//if (s1==s2) //Remove these lines
//button.setVisibility(View.INVISIBLE);
}
You should call this line:
if (s1==s2) button.setVisibility(View.INVISIBLE);
inside your onDataChange:
#Override
protected void onStart() {
super.onStart();
control.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
GenericTypeIndicator<String> s = new GenericTypeIndicator<String>() {};
s2=dataSnapshot.getValue(s);
if (s1==s2) //you can change this line with "if (s1.equals(s2))"
button.setVisibility(View.INVISIBLE);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {}
});
}
because addValueEventListeneris asynchronous, so onDataChange will be called after onStart is finished

Firebase execute code IF child has been created

I have the following code for deleting some data form Firebase:
public void removeGameFromWaitQueue() {
final DatabaseReference data = FirebaseDatabase.getInstance().getReference().child("games").child(mCurrentGameKey);
data.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.exists()) {
mCurrentGame = snapshot.getValue(DatabaseModels.Game.class);
if (mCurrentGame.getState() == DatabaseModels.Game.State.OPEN.ordinal()){
data.removeValue();
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
How can i check if "data" exists, then execute all the else code? Meaning that if i create the DatabaseReference, code works. But of course if i dont create the reference, then i have null exceprion. Should i have 2 listeners for this? And if yes, how is the best way to do it?
For future reference i solved it like this:
public void removeGameFromWaitQueue() {
final DatabaseReference data = FirebaseDatabase.getInstance().getReference().child("games").child(mCurrentGameKey);
data.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.exists()) {
mCurrentGame = snapshot.getValue(DatabaseModels.Game.class);
if (mCurrentGame.getState() == DatabaseModels.Game.State.OPEN.ordinal()){
data.removeValue();
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
public void removeAfterCheck(){
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
rootRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.hasChild("games")) {
removeGameFromWaitQueue();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
Probably there is a better way, but this works for me in all cases as i want.

firebase gets data too many times

I used recyclerview, arraylist, and firebase to get and show data, but data is added to the list too many times..
This is my code
database=FirebaseDatabase.getInstance();
mRef=database.getReference("SETS");
mRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot==null) return;
for(DataSnapshot userSanpshot : dataSnapshot.getChildren())
{
if(userSanpshot.child("DETAIL").getValue()==null) break;
single.Detail=userSanpshot.child("DETAIL").getValue().toString();
/~/
list.add(single);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
adapter.setItems(list);
and this is my code when I send data to firebase
Button sharebut=(Button)findViewById(R.id.usersend);
sharebut.setOnClickListener(
new FloatingActionButton.OnClickListener() {
#Override
public void onClick(View view) {
mDatabase.child("SETS").child(userId).child("DETAIL").setValue(detailText.getText().toString());
/~/
finish();
}
}
);
}
Try Using this method
mRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// do here your work
}
#Override
public void onCancelled(DatabaseError databaseError) {
// handle error here
}
});

How to execute firebase function synchronously inside for loop?

I am retrieving data from firebase database inside for loop , but problem is that firebase functions are not execute synchronously , i know firebase functions are asynchronous . is there any solution for that ?
Code :-
registrationReference.child(userId).child("generated_links").addListenerForSingleValueEvent(new ValueEventListener()
{
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
peopleModelList.clear();
for(DataSnapshot snapshot : dataSnapshot.getChildren())
{
peopleModel = new PeopleModel();
peopleHashMap =(HashMap)snapshot.getValue();
peopleModel.setChatWith((String)peopleHashMap.get("chatWith"));
peopleModel.setNickname((String)peopleHashMap.get("nickname"));
peopleModel.setChatRoom(snapshot.getKey());
Log.d("kkkk","1st");
if(peopleModel.getChatWith()!=null && !("".equals(peopleModel.getChatWith())))
{
registrationReference.child(peopleModel.getChatWith()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
peopleModel.setRefresh_token((String)dataSnapshot.child("refresh_token").getValue());
peopleModelList.add(peopleModel);
Log.d("kkkk","2nd");
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
Log.d("kkkk","3rd");
}
iCallBackPeople.peopleAct(peopleModelList);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Output :-
1st
3rd
2nd
But i want output like this
1st
2nd
3rd
Note :-
i searched , but never got any solution !
How to get data from Firebase Database in a loop?
Firebase addListenerForSingleValueEvent excute later in loop
I tried this but not worked for me, it is fill my list with last item !
public void listOfUsers(final ICallBackPeople iCallBackPeople) {
count=0;
//peopleModelList = new ArrayList<>();
sortedMap = new TreeMap<>();
// peopleModelList = new ArrayList<PeopleModel>(sortedMap.values());
registrationReference.child(userId).child("generated_links").addListenerForSingleValueEvent(new ValueEventListener()
{
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// peopleModelList.clear();
for(DataSnapshot snapshot : dataSnapshot.getChildren())
{
peopleModel = new PeopleModel();
peopleHashMap =(HashMap)snapshot.getValue();
peopleModel.setChatWith((String)peopleHashMap.get("chatWith"));
peopleModel.setNickname((String)peopleHashMap.get("nickname"));
peopleModel.setChatRoom(snapshot.getKey());
//Object chatRoom = snapshot.getKey();
Log.d("kkkk","1st");
if(peopleModel.getChatWith()!=null && !("".equals(peopleModel.getChatWith())))
{
addItem(count,iCallBackPeople);
count++;
}
Log.d("kkkk","3rd");
}
Log.d("kkkk","end");
//iCallBackPeople.peopleAct(peopleModelList);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
private void addItem(final int index, final ICallBackPeople iCallBackPeople) {
registrationReference.child(peopleModel.getChatWith()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
peopleModel.setRefresh_token((String)dataSnapshot.child("refresh_token").getValue());
Log.d("kkkk","2nd");
sortedMap.put(index, peopleModel);
// sortedMap will sort your list by key (in this case, key is integer)
if(sortedMap.size()==2)
{
peopleModelList = new ArrayList<PeopleModel>(sortedMap.values());
iCallBackPeople.peopleAct(peopleModelList);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
I tried this but not getting all of the item only getting list with last item in snapshot
public void listOfUsers(final ICallBackPeople iCallBackPeople) {
peopleModelList = new ArrayList<>();
registrationReference.child(userId).child("generated_links").addListenerForSingleValueEvent(new ValueEventListener()
{
PeopleModel peopleModel;
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
peopleModelList.clear();
for(final DataSnapshot snapshot : dataSnapshot.getChildren())
{
peopleModel = new PeopleModel();
peopleHashMap =(HashMap)snapshot.getValue();
peopleModel.setChatWith((String)peopleHashMap.get("chatWith"));
peopleModel.setNickname((String)peopleHashMap.get("nickname"));
peopleModel.setChatRoom(snapshot.getKey());
if(peopleModel.getChatWith()!=null && !("".equals(peopleModel.getChatWith())))
{
getToken(new CallBackGetToken() {
#Override
public void token(String token) {
peopleModel.setRefresh_token(token);
Toast.makeText(context, token, Toast.LENGTH_SHORT).show();
peopleModelList.add(peopleModel);
//here i am checking my peopleModelList size is equal or not to snapshot
if(peopleModelList.size()==2)
iCallBackPeople.peopleAct(peopleModelList);
}
},peopleModel);
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
private void getToken(final CallBackGetToken callBackGetToken ,PeopleModel peopleModel) {
registrationReference.child(peopleModel.getChatWith()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
callBackGetToken.token((String)dataSnapshot.child("refresh_token").getValue());
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
I think you should pass through an interface(create one and implement at this class). the method inside the interface should have a string as a parameter (it will receive your peopleModel.getChatWith() ) .
Inside your if(peopleModel.getChatWith()!=null && !("".equals(peopleModel.getChatWith())))you should request your interface.
I think is a solution for your asynchronously question.
Here is my solution:
int count =0;
SortedMap<Integer,PeopleModel> sortedMap;
private void set() {
count =0;
registrationReference.child(userId).child("generated_links").addListenerForSingleValueEvent(new ValueEventListener()
{
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
peopleModelList.clear();
sortedMap = new TreeMap<>();
for(DataSnapshot snapshot : dataSnapshot.getChildren())
{
peopleModel = new PeopleModel();
peopleHashMap =(HashMap)snapshot.getValue();
peopleModel.setChatWith((String)peopleHashMap.get("chatWith"));
peopleModel.setNickname((String)peopleHashMap.get("nickname"));
peopleModel.setChatRoom(snapshot.getKey());
Log.d("kkkk","1st");
if(peopleModel.getChatWith()!=null && !("".equals(peopleModel.getChatWith())))
{
addItem(count);
count++;
}
Log.d("kkkk","3rd");
}
iCallBackPeople.peopleAct(peopleModelList);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
private void addItem(final int index) {
registrationReference.child(peopleModel.getChatWith()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
peopleModel.setRefresh_token((String)dataSnapshot.child("refresh_token").getValue());
Log.d("kkkk","2nd");
sortedMap.put(index, peopleModel);
// sortedMap will sort your list by key (in this case, key is integer)
// you can get peopleModelList = new ArrayList<>(sortedMap.values);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
Maybe I'm over simplifying it, but I feel like you should just use simple recursion.
List<People> peopleList;
int currentIndex = 0
private void myFirebaseMethodAction(person){
registerListener.onCallbacks(new ValueListener(){
onDataChanged(){
person.addStufforDoStuff();
if(peopleList.size > currentIndex + 1){
myFirebaseMethodAction(peopleList.get(++currentIndex)
}
}
onFailed(){
//handle it
}
}
}
Does that make sense?
Just saying walk the list with an index, simple.
btw above is pseduo code, don't copy verbatim lol.

Categories

Resources