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
Related
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).
I'm creating an Android app for the first time, I've got a simple Realtime Firebase Database with a couple of records in it. I have the following code;
public void onStart() {
super.onStart();
// Read from the database
databaseMatches.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot matchSnapshot : dataSnapshot.getChildren()) {
matches match = matchSnapshot.getValue(matches.class);
matchesList.add(match);
}
matchList adapter = new matchList (getActivity(), matchesList);
listViewMatch.setAdapter(adapter);
}
#Override
public void onCancelled(DatabaseError error) {
// Failed to read value
Log.w(TAG, "Failed to read value.", error.toException());
}
});
If I put a breakpoint on the databaseMatches.addValueEventListener(new ValueEventListener() { it shows me that the database connection has been set and is returning the correct object (In my view).
The challenge I have is the part after, the break points for public void onDataChange nor onCancelled ever get hit. I'm lost here and not sure what might be the next step as it appears to be connecting, but I am not able to retrieve records.
I'm doing this in a fragment instead of a activity. Any help is appreciated.
Detecting Connection State
it is useful for your app to know when it is online or offline. Firebase Realtime Database provides a special location at /.info/connected which is updated every time the Firebase Realtime Database client's connection state changes. Here is an example: If you are not sure.
https://firebase.google.com/docs/database/android/offline-capabilities#section-connection-state
DatabaseReference connectedRef =
FirebaseDatabase.getInstance().getReference(".info/connected");
connectedRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
boolean connected = snapshot.getValue(Boolean.class);
if (connected) {
System.out.println("connected");
} else {
System.out.println("not connected");
}
}
#Override
public void onCancelled(DatabaseError error) {
System.err.println("Listener was cancelled");
}
Firebase also loads and synchronizes data asynchronously
see Setting Singleton property value in Firebase Listener
Thanks.
There must have been some strange caching issue as the following morning when I ran the exact same code, no problem. And I've not had a problem since.
I am developing an android app using Firebase Realtime Database.
My addValueEventListener for a specific reference runs whenever there is a data change in the database. It also runs whenever the app starts. These make sense to me, but this even runs when I return to my app from a different activity. For example, it runs when I go to the HOME screen of my phone and go back to this app, the addValueEventListener runs.
This is a problem for me because I am implementing a vibration whenever there is a data change in my app. I put a global int variable to prevent vibration on the first time i get to the app.
It works like:
matchReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(notificationCounter > 0) notifyGoals(getView());
clearCurrentList();
notificationCounter++;
// Fires every single time the channelReference updates in the
}
adapter.notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
I initialize int notificationCounter = 0 outside of this function so that it gets reset to 0 whenever I re-run this app. But, whenever I go to a different activity on my phone (HOME Screen or LOCK screen) and return to this screen, it runs the addValueEventListener before the int notificationCounter = 0 initialization. How can I solve this problem?
This is happening because onDataChange() method is called asynchronous which means that this method does not wait to get the data from your database and is called even before. To solve this, you have 2 choices. One would be to move the declaration of your notificationCounter int inside the onDataChange() method or second, to dive into the asynchronous word and use my answer from this post.
Just remove your valueListener in activity pause
private HashMap<DatabaseReference, ValueEventListener> hashMap = new HashMap<>();
private ValueEventListener valueEventListener;
mChatReference.child(groupID).addValueEventListener(valueEventListener=new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
#Override
public void onBackPressed() {
hashMap.put(mChatReference.child(groupID), valueEventListener);
removeValueEventListener(hashMap);
finish();
}
public void removeValueEventListener(HashMap<DatabaseReference, ValueEventListener> hashMap) {
for (Map.Entry<DatabaseReference, ValueEventListener> entry : hashMap.entrySet()) {
DatabaseReference databaseReference = entry.getKey();
ValueEventListener valueEventListener = entry.getValue();
databaseReference.removeEventListener(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.
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.