Firebase Database Datasnapshot - android

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.

Related

How to use a value outside dataSnapshot function?

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

Firebase Realtime Database can you remove Listener when Listener is called

So I am using Firebase Realtime Database and I want to remove a listener as soon as a certain criteria is met. Here is my code:
final DatabaseReference forRequests = FirebaseDatabase.getInstance().getReference(Common.requests + "/" + FirebaseAuth.getInstance().getUid());
listenForRequests = forRequests.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
pickuprequest.riderUID = (String) dataSnapshot.child(Common.riderUID).getValue();
if (pickuprequest.riderUID != null) {
forRequests.removeEventListener(listenForRequests);
showRequestOnMap(forRequests);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
System.out.println("The read failed: " + databaseError.getCode());
}
});
}
listenForRequests is a global variable. Just wondering if this code will work, or if there are any better solutions to this as I feel like it is very hacky.
I can't really tell what exactly your condition is really trying to express, but if you want just a single value a single time from the database (without listening to its changes over time), you can simply use addListenerForSingleValueEvent() to get a single snapshot of a node in the database.
If you're waiting for a value to appear that wasn't previously there, and you want to stop listening at the time it appears, what you're doing is fine. But you might want to listen more closely to the child of interest instead of its parent.
Your code looks pretty idiomatic to me when you want to wait for a specific value.
In fact, the code in my gist on waiting for an initial value is pretty similar:
mListener = ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.exists()) {
System.out.println("The value is now "+snapshot.getValue());
ref.removeEventListener(mListener);
}
}
...

How do I check if specific child value exists in FireBase (Android)

I have some trouble trying to check if user information is stored already in the FireBase database.
Basically I'm trying to do something stupid like this:
"select user_name from user where user_id="+userID+"
And if the nickname exists it should make the boolean var isFirstTime = false and if it doesn't it should stay true. And after that it should show register box or not.
This is my db:
Firebase
And this is my code in onCreate method:
databaseReference = FirebaseDatabase.getInstance().getReference();
DatabaseReference dbRefFirstTimeCheck = databaseReference.child("User").child(user.getUid()).child("Nickname");
isFirstTime = true;
dbRefFirstTimeCheck.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.getValue() != null) {
isFirstTime=false;
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
if(isFirstTime) {
showNewUserBox();
}
else {
}
No matter what I do, the methor showNewUserBox() is being called. How do I get the data i need and check if it's there?
As others have commented, data is loaded from Firebase asynchronously. By the time you check isFirstTime, the data hasn't been loaded yet, onDataChange hasn't been run yet, so ifFirstTime will have its default value (false for a boolean).
All code that requires data from the database should be inside onDataChange (or invoked from within there). The simplest fix for your code is:
databaseReference = FirebaseDatabase.getInstance().getReference();
DatabaseReference dbRefFirstTimeCheck = databaseReference.child("User").child(user.getUid()).child("Nickname");
dbRefFirstTimeCheck.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.exists()) {
showNewUserBox();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore errors
}
});
Also see some of the many questions about asynchronous loading from Firebase, such as getContactsFromFirebase() method return an empty list (or this quite old classic: Setting Singleton property value in Firebase Listener).

Android firebase count element

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.

Read Firebase Data Once With ValueEventListener

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.

Categories

Resources