I have been trying to set a value change listener like this:
final DatabaseReference chat_ref =
FirebaseDatabase.getInstance().getReference(Constants.ARG_CHAT_ROOMS +
"/" + room_type_1);
chat_ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(final DataSnapshot dataSnapshot) {
showAlert(dataSnapshot);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
The problem is that, the parenthesis after the showAlert method keeps looping over, creating several alerts when I only want one.
What could be causing this to loop and how can I address it?
Your help will be most appreciated!
I am not sure where I am getting it wrong, because even this loops as well:
chat_ref.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
showAlert(dataSnapshot);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
You have those several alerts because of the way in which ValueEventListener works. But remember ValueEventListener is not a loop is an interface. Please see more details here. And as you see in the offical doc, it is triggered every time the data at a particular location changes.
To solve this, just get the call of the showAlert() out from the onDataChange method.
Hope it helps.
If you add ValueEventListener with addValueEventListener it will fire every time value of you reference changes so showAlert can be displayed several times.
Use addListenerForSingleValueEvent: your listener will be called only once.
https://firebase.google.com/docs/database/android/read-and-write#read_data_once
Related
In the code below i get the number of children but i want to use it outside the onDataChange method.
mRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot)
{
DateStorage dateStorage = null;
for (DataSnapshot result : dataSnapshot.getChildren())
{
Log.e(result.getKey(),result.getChildrenCount() + "");
in[0] = result.getChildrenCount();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Can anyone help me?
Data is loaded from Firebase asynchronously. Your main code continues to run while the data is loading, and then when the data is available the onDataChange method is called. What that means is easiest to see if you add a few log statements:
Log.d("TAG", "Before attaching listener");
mRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot)
{
Log.d("TAG", "Got data");
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
});
Log.d("TAG", "After attaching listener");
When you run this code, it logs:
Before attaching listener
After attaching listener
Got data
This is probably not the order you expected, but is completely normal when calling asynchronous APIs. And it explains why you'll get the wrong value if you print it outside of the onDataChange().
The problem is not that you can't use the data outside of the onDataChange(), the problem is that you must ensure that onDataChange() has run before you use the data.
The simplest way to do that is to put all code that requires data from the database inside the onDataChange method. But you can also create your own callback interface, and pass that into the method where you load the data. For an example of both of these approaches, see my answer here: getContactsFromFirebase() method return an empty list
I want to count elements in a firebase database, I have seen different topics and tried this code:
final Query dataQuery = myRef.equalTo(MainActivity.user.getUid()).getRef();
dataQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.e("ERROR",""+dataSnapshot.child(MainActivity.user.getUid()).getChildrenCount());
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e(TAG, "onCancelled", databaseError.toException());
}
});
Now, the value in log error is correct but, if I try to assign it in field, or static field is always 0 out of this method;
How can I use this value in other class?
You need to use a callback and call a method on the callback in your onDataChange. Then once that callback is returned you can continue with the rest of your logic.
You can see an example of that here:
https://github.com/Austin-Android/austin-feeds-me/blob/master/app/src/main/java/com/austindroids/austinfeedsme/data/firebase/FirebaseEventsDataSource.java#L40
fireBase.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
Event event = snapshot.getValue(Event.class);
events.add(event);
}
callback.onEventsLoaded(events);
}
#Override
public void onCancelled(DatabaseError firebaseError) {
}
});
You cannot simply take that value and use it outside onDataChange() method, because it will always be null. This is happening because this method has an asynchronous behaviour, which means that is called even before you are getting the data out from the database. A quick fix would be to use that value only inside onDataChange() method, or to dive into the asynchronous world and see the last part of my answer from this post.
Within my app I often have the need to read data once. I originally started by using the addListenerForSingleValueEvent() method for this, however I ran into problems using this method as it does not work as I wanted when offline capabilities are enabled (see here the issue: Firebase Offline Capabilities and addListenerForSingleValueEvent)
In the question above it is mentioned that a workaround is to use the addValueEventListener() method, however I do not fully understand how to do this (particularly how to remove the ValueEventListener as soon I am finished grabbing the data I need).
Take this method which I created in a standalone class to query the Users node on Firebase where I store the users FCM Token. It seems to have an issue of not returning the latest token from the server everytime.
public class SendFCMMessage {
String userToken;
String currentUser;
String userName;
ValueEventListener userListener;
public void sendMessage(final String contactNumber) {
final DatabaseReference ref = FirebaseDatabase.getInstance().getReferenceFromUrl(link).child("Users").child(contactNumber);
userListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
userToken = user.getToken();
// Send FCM Message after getting user token and then remove event listener
ref.removeEventListener(userListener);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.d("TAG", "Something terrible went wrong: " + databaseError);
}
};
ref.addValueEventListener(userListener);
}
}
If I remove the line
ref.removeEventListener(userListener);
Then this code works fine, however I would like to know how I could remove the ValueEventListener as soon as I receive the data I need?
Thanks,
R
ValueEventListener vel; //Declared Global
Listen your DatabaseReference like this;
vel = yourDatabaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChanged(DataSnapshot dataSnapShot) {
//Do your stuff here. I suggest you create another method for this if you don't want a problem with inner class.
//For example, workDataSnapshot(DataSnapshot dataSnapShot) <-- Work here
yourDatabaseReference.removeEventListener(vel);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Hope it helps you.
I have a firebase database from which I save and retrieve data from, to and from. I know how datasnapshot works inside an addValueEventListener. The problem is that this is only called or triggered when the firebase database detects change in its data. I only want to access data and read it to be able to store it in an arraylist or the same thing.
I have a code like this:
public void foo(){
DatabaseReference x= FirebaseDatabase.getInstance().getReference().child("x");
reservations.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String userID = client.getId();
for(DataSnapshot snap : dataSnapshot.getChildren()){
if(snap.child("someId").equals(someId)) number++;
if(snap.child("userID").getValue().equals(client.getId())){
isAlreadyReserved = true; // if user has already reserved the item
alreadyReserved();
break;
}
Log.e("isAlreadyReserved: ", isAlreadyReserved+"");
numberOfReservations++;
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
if(isAlreadyReserved) {
alreadyReserved();
}
else if(number == numberOfCopies){
// material is no longer available
OtherActivity.showMaterialUnavailable();
}
else{
Reservation reservation = new Reservation();
reservation.setBookId(this.bookId);
reservation.setResID((numberOfReservations+1)+"");
reservation.setUserID(client.getId());
String key = reservations.push().getKey();
reservations.child(key).setValue(reservation);
Log.e("Reservations: ", "reserve successful");
AlertDialog.Builder builder = new AlertDialog.Builder(this.context);
builder.setTitle(R.string.reservationSuccess_title)
.setMessage(R.string.reservationSuccess_body);
AlertDialog dialog = builder.create();
dialog.show();
}
}
You can see that inside onDataChange I only count materials and set some flags, which I can supposedly do outside the ValueEventListener.
But I notice that this is faulty because onDataChange is called only when writing to the Firebase database occurs. Which should not be the case.
What can I do to loop through the values inside the DatabaseReference x without calling onDataChange, or without using DataSnapshot?
You cannot loop inside a DatabaseReference without using a listener. When we are talking about Firebase, we are talking only about listeners. So in order to get those values, you need to use a listener and than get the data out from the dataSnapshot.
What i think your problem is in your case, is that onDataChange method is called in an asynchronously way. This means that everything you are doing outsite this method is actually executed before onDataChange method has been called. So in order to understand what is actually going on, please see this post and this post. Reading this posts, will teach you how to query data in Firebase and how to retrieve data from Firebase asynchronously.
Hope it helps.
In order to get the values of DatabaseReference x, you should use addListenerForSingleValueEvent
x.addListenerForSingleValueEvent(new ValueEventListener()
{
#Override
public void onDataChange(DataSnapshot dataSnapshot)
{
//do something
}
#Override
public void onCancelled(DatabaseError databaseError)
{
//do something
}
});
as mentioned in the firebase documentation:
public void addListenerForSingleValueEvent (ValueEventListener
listener)
Add a listener for a single change in the
data at this location. This listener will be triggered once with the
value of the data at the location.
As the document says, listener for SingleValueEvent only run one time.
Then is it unnecessary to remove listener after using it like this?
final Query query = getChatsRef().limitToLast(20);
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
query.removeEventListener(this);
}
#Override
public void onCancelled(DatabaseError databaseError) {
query.removeEventListener(this);
}
});
No. Removing the listener as you do in your snippet of code is not needed.
The only reason you might want to remove a once listener, is when the listener hasn't fired yet. The only time I can see that happening is when you're not connected to the Firebase servers and the location you're inspecting is not cached. That should be a fairly small number of cases.