I have nodejs server and an android client. Both are connected to my firebase database. When the server sets the "RefNode" to null, the onDataChanged Callback, that is set on that "RefNode" on the andriod side, gets called.
Is there any way to avoid it being called in the special case when the "RefNode" was set to null while for all other cases it should get called.
I have a listener on a node "RefNode" in my android application as given below:
The OnDataChanged() callback is getting called even when the "RefNode" was set to null by the nodejs server.
FirbaseDatabase.getInstance().getReference().child("RefNode").addValueEventListener( new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.exists()){
if(dataSnapshot.getValue()!=null) {
Log.v(TAG, "got here even when the Refnode was set to null");
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
// The listener got cancelled
}
});
I have found the problem I had attached the event listener twice. I had made the ValueEventListener a field of the class the and attached it twice.
Related
I want to send a notification whenever the data in firebase changes.
But when I use addValueEvenListener() method it returns more than once. After that I tried using addListenerForSingleValueEvent() method but it now returns 2 times, When I start the app and when the data changes. Is there a way for it to return only one time which is when the data changes and not when the app starts?
Here is my code for now:
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
sendNotification("notification","App name",getIntent().getStringExtra("storeid"));
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
EDIT:
Just so every one understand my question.
When I start the app, a notification is sent because of the code above. And I don't want this to happen, Instead I need to only send the notification when the data changes.
Let's say you have one boolean variable
declared globally in the class where you register the listener
boolean isCalled=false;
Reset it just before registering the listener.
isCalled=false;
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
if(isCalled){
//it is already called once.
sendNotification("notification","App name",getIntent().getStringExtra("storeid"));
}else{
//called first time
isCalled=true;
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
According to the Docs, the ValueEventListener returns a Value not just when the data is changed but also when the method is first executed.
As far as I can tell, there are two possible solutions to your problem:
You use a background service in order to keep the method running in the background so that it does not return a value for each time you open the app. This would also mean, that you would get notifications when the app is not even open. I don't know if that is in your interest.
Store the value and check it manually each time. You can save the returned value to the storage and check if the new value is different from the prior value each time the listener executes.
I hope I could help, happy coding
Data stored in the Firebase Realtime Database is retrieved by attaching it to an asynchronous listener data source. The listener is triggered again once for its initial state and each time its data changes. In your case, no clear solution is presented in the documentation.
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'm facing an issue regarding the Firebase Realtime database and in particular the value event listener which fires more than once .Seems that when the Internet state changes from on to off several times and the device finally has stable connection the onDataChange(DataSnapshot dataSnapshot) callback method of the listener is invoked with dataSnapshot of null content.Seems that the Realtime Database refers to the app's local cache and in that case I do not have any data stored in it. I am attaching the listener inside the Activity onStart() or when the device has established some connection ; I am detaching the listener inside the Activity onStop() method or when the device looses internet connection .Only one instance of a given listener exists at a time and every attach has corresponding detach action executed when needed. I have tried to wait a while between the change of the connection states before attaching the listener and to reattach the listener if the datasnapshot returns null .None of those worked.Please advice for a solution.
Some example code inside an Activity :
private ValueEventListener listener;
private Query query;
private boolean hasAttachedListener;
private Query getDatabaseReference() {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference();
return reference.child(“some child ref”)
.child(“other child ref 2 ”);
}
private ValueEventListener getDatabaseListener() {
return new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.d(“firebase”, dataSnapshot.toString());
//issue here datasnapshot is null sometimes
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.d(“firebase”, databaseError.getDetails());
}
};
}
/**
* Attaches listener
*/
public void addListener() {
if (!hasAttachedListener) {
query = getDatabaseReference();
listener = getDatabaseListener();
query.addValueEventListener(listener);
hasAttachedListener = true;
}
}
/**
* Detaches the attached listener
*/
public void removeListener() {
if (hasAttachedListener) {
query.removeEventListener(listener);
query = null;
listener = null;
hasAttachedListener = false;
}
}
#Override
protected void onStart() {
super.onStart();
addListener();
}
#Override
protected void onStop() {
super.onStop();
removeListener();
}
#Override
protected void onNetworkDisconnected() {
super.onNetworkDisconnected();
// invoked when internet connection is lost
removeListener();
}
#Override
protected void onNetworkReconnected() {
super.onNetworkReconnected();
// invoked when internet connection is restored
addListener();
}
With firebase offline capabilities you are not needed to use those two method to listen if there is no connection to the database
so your onNetworkDisconnected and onNetworkReconnected are not necesary
check the firebase docs here : https://firebase.google.com/docs/database/android/offline-capabilities
Keeping Data Fresh
The Firebase Realtime Database synchronizes and stores a local copy of the data for active listeners. In addition, you can keep specific locations in sync.
DatabaseReference scoresRef = FirebaseDatabase.getInstance().getReference("scores");
scoresRef.keepSynced(true);
The Firebase Realtime Database client automatically downloads the data at these locations and keeps it in sync even if the reference has no active listeners. You can turn synchronization back off with the following line of code.
The new data should have unique name to prevent replacement of existed
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.
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.