Good evening.
I have a strange behavior with Firebase realtime database for Android.
I'll try to code a simple note app. My data structure is very simple:
root
notes
uid
key
archived
color
date
text
last_update
Please, keep in mind last_update field.
I have the correct database reference in a service. In the onCreate I add a ValueEventListener to my reference. I choose this listener because I want that when the service is created, synck each note for that logged user.
Inside the onValueChanged method, after data handling, I remove the ValueEventListener and I add the ChildEventListener, for handle CRUD operations (adding, removing, updating).
Until now everything works great.
My code is:
#Override
public void onCreate(){
...
database = FirebaseDatabase.getInstance().getReference().child("notes" + "/" + "current_user_uid");
database.addValueEventListener(valueEventListener);
}
So, my ValueEventListener is:
private ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.d(TAG, "main --> " + dataSnapshot.toString());
...//some stuff for saving data in a local sqlite db, nothing related to my problem
database.removeEventListener(this);
database.addChildEventListener(childEventListener);
//so here I have removed the ValueEventListener and I have attached a ChildEventListener.
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.d("firebase", databaseError.toString());
}
};
Ok, my ChildEventListener is:
private ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Log.d(TAG, "add --> " + dataSnapshot.toString());
...//code for saving on my local db
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
Log.d(TAG, "update --> " + dataSnapshot.toString());
...//code for updating on my local db
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Log.d(TAG, "delete --> " + dataSnapshot.toString());
...//code for deleting from my local db }
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
Log.d(TAG, "moved --> " + dataSnapshot.toString());
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.d(TAG, "error --> " + databaseError.toString());
}
};
Ok, until now, everything works.
Problems starts when I have tried to exclude notes already received. I use this code:
database.orderByChild(last_update).startAt(time).addValueEventListener(valueEventListener);
obviously, the variable time is 0 for the first call, then I use SharedPreferences for save the value of System.currentTimeMillis inside each callback. I do the same for the ChildEventListener. Also, when a note is created/modified I use the value of System.currenTimeMillis for the field last_update.
So, if the logged user has no notes, everything works. But if there are some notes (for example, if I take a couple of notes and reboot the service), I don't receive the notes with last_update < time (correct), but when I try to update a note using:
childReference.child(uid).updateChildren(HashMap);
the onNoteUpdated callback is not called, is called onNoteAdded! Sometimes onNoteUpdated is called after onNoteAdded. But if I try to update several times a note, finally, after at least one call to onNoteAdded, onNoteUpdated works again.
I really don't understand why. Any suggestions?
Thanks.
Related
I'm trying to connect the Firebase real-time database as a database to my app's chats. I know that I can limit the entries that I access to last 10 entries by calling limitToLast(10) function to my Firebase reference.
The problem I have right now is that when I do this, it will limit that reference to always display the last 10 entries even if I add more entries from my app. However, I would want it to count up as I add more entries to my app. So, for instance, if I add 2 more entries, my chat list would show 12 entries and so on.
Here's my code so far with that limitToLast(10) added.
DatabaseReference tRef = database.getReference(getString(R.string.firebase_channel));
tRef.limitToLast(10).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
Chat c = dataSnapshot.getValue(Chat.class);
chatAdapter.newChat(c, dataSnapshot.getKey());
Log.d(TAG, "onChildAdded\t" + c.asChat());
}
#Override
public void onChildChanged(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
Chat c = dataSnapshot.getValue(Chat.class);
chatAdapter.modifyChat(c, dataSnapshot.getKey());
Log.d(TAG, "onChildChanged\t" + c.asChat());
}
#Override
public void onChildRemoved(#NonNull DataSnapshot dataSnapshot) {
Chat c = chatAdapter.removeChat(dataSnapshot.getKey());
Log.d(TAG, "onChildRemoved\t" + c.asChat());
}
#Override
public void onChildMoved(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
Chat c = dataSnapshot.getValue(Chat.class);
chatAdapter.moveChat(c, dataSnapshot.getKey());
Log.d(TAG, "onChildMoved\t" + c.asChat());
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.d(TAG, "onCancelled\t" + databaseError.toException());
Toast.makeText(context, "error loading chat list", Toast.LENGTH_SHORT).show();
}
});
What came through my mind is to actually separate the chat lists into one with which I show in my app and one with which is connected to Firebase, so when a new entry is added in my app, I'll add that to both lists. But I really don't think that using up this extra memory is the solution to this issue. Is there any way to resolve this issue otherwise?
If you only ever want to add items, how about simply ignoring onChildRemoved?
I'm implementing a search in my android app and I can't seem to make it work.
public void loadReleaseData(String name) {
mDatabase.child("releases")
.child("europe")
.child("data").orderByChild("game/name").startAt(name)
.endAt(name+"\uf8ff")
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.hasChildren()) {
Log.d(TAG, "New datasnapshot");
for (DataSnapshot data : dataSnapshot.getChildren()) {
_Release release = data.getValue(_Release.class);
if (release != null) {
// No platform filter set add all releases!
list.add(release);
if (release.getGame() != null) {
Log.d(TAG, "NAME: " + release.getGame().getName());
}
}
mUpcomingGamesAdapter.notifyDataSetChanged();
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
When I remove startAt or endAt either of the two data is shown but not the correct data, but when they're both added in, nothing is show and my log "New datasnapshot" doesn't even get printed. I'm searching on the names of the games I have in my database.
My firebase database:
According to your comments, the reason your code was not working was because the value of the name variable that was passed to startAt() and endAt methods was incorrect.
The key for solving the problem is to pass as an argument to both method the exact same name that exist in the database, in this case you should search the name in lower case.
I'm making an app and I've run into a problem with Firebase's Database and Authentication services.
After a while, while I am authenticated (using Firebase Auth) and using my app, my database ValueEventListeners don't seem to be called, even though there is data in the database.
How I'm adding my listeners:
FirebaseDatabase
.getInstance()
.getReference()
.child("my_child")
.addValueEventListener(new ValueEventListener()
{
#Override
public void onDataChange(DataSnapshot dataSnapshot)
{
Log.d("App", "onDataChange");
}
#Override
public void onCancelled(DatabaseError databaseError)
{
}
});
Stuff I've tried:
Checking the database rules (after a relog reading is fine - but simulated reads pass even while authenticated & unauthenticated)
keepSynced(true) on the DatabaseReferences
Adding the listeners in the Activity's onCreate instead of the Application's onCreate
Adding/removing/updating data in the database to trigger a sync
Rebooting
Any help would be much appreciated.
So, apparently the issue was that an API called "Token Service" was not enabled in my Google APIs dashboard.
Thanks to a helpful email from Firebase Support (thanks guys!), I've turned on debug logging by calling FirebaseDatabase.getInstance().setLogLevel(Logger.Level.DEBUG);
Lo and behold: D/PersistentConnection: pc_0 - Error fetching token: An internal error has occurred. [ �Token Service API has not been used in project <project-id> before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/securetoken.googleapis.com/overview?project=<project-id> then retry.
So by enabling the API, it seems to have fixed the error!
Create a reference to your child database, create a custom Chat class as per your requirement( essentially what you see in your firebase ).
addChildEventListener should give you all the changes happening with your my_child. Hope this helps you.
mFirebaseRef = new Firebase("https://yourapp.firebaseio.com/").child("my_child");
/**
* Firebase - Receives message
*/
mFirebaseRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
if (dataSnapshot != null && dataSnapshot.getValue() != null) {
try{
Chat model = dataSnapshot.getValue(Chat.class);
mChats.add(model);
mRecyclerView.scrollToPosition(mChats.size() - 1);
mAdapter.notifyItemInserted(mChats.size() - 1);
} catch (Exception ex) {
Log.e(TAG, ex.getMessage());
}
}
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
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 am adding new data to Firebase as following code
updateChildren(childUpdates, new DatabaseReference.CompletionListener() {
#Override
public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
}
}
And following code for ChildEventListener
fb.child(Organizations.class.getSimpleName().toLowerCase()).limitToLast(1).addChildEventListener(crudListener);
ChildEventListener crudListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Log.e(TAG,"onChildAdded "+ dataSnapshot.toString()+" \n String "+s);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
Log.e(TAG,"onChildChanged "+ dataSnapshot.toString()+" \n String "+s);
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Log.e(TAG,"onChildRemoved "+ dataSnapshot.toString());
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
Log.e(TAG,"onChildAdded "+ dataSnapshot.toString()+" \n String "+s);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
issue is when I am creating new data then onChildRemoved() triggered before onChildAdded()
I am not removing any child but onChildRemoved() triggered every time when new child is created
following Web(javascript) code it's working fine
firebase.initializeApp(config);
var commentsRef = firebase.database().ref('organizations').limitToLast(1);
commentsRef.on('child_added', function(snap) {
$('pre').text("child_added: "+JSON.stringify(snap.val(), null, 2));
});
commentsRef.on('child_changed', function(snap) {
$('pre').text("child_changed: "+JSON.stringify(snap.val(), null, 2));
});
commentsRef.on('child_removed', function(snap) {
$('pre').text("child_removed: "+JSON.stringify(snap.val(), null, 2));
});
here is my Database structure. I am creating new child under organizations
Pleaes help me how to fix this issue
You mentioned you're using limitToLast(1) when attaching crudListener. That's actually how it works.
from Quer#limitToLast() reference
The limitToLast() method is used to set a maximum number of children to be synced for a given callback. If we set a limit of 100, we will initially only receive up to 100 child_added events. If we have less than 100 messages stored in our database, a child_added event will fire for each message. However, if we have over 100 messages, we will only receive a child_added event for the last 100 ordered messages. As items change, we will receive child_removed events for each item that drops out of the active list, so that the total number stays at 100.
If you use limitToLast(1), the onChildRemoved() will be called if a new child is inserted to maintain the limit (which is 1), and then onChildAdded() called the new child.
If you don't want this behavior, remove the limitToLast(1) method
EDIT:
If you want to keep using limitToLast(1), then you have to do checking if an organization entry is really removed from the database or it's from the limit behavior.
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
dataSnapshot.getRef().addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (!dataSnapshot.exists()) {
// remove this organization item from the RecyclerView
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
I had similar "unexpected" behaviour connected to onChildRemoved, when I used filtering operators orderByChild("someKey").equalTo(someValue).
Being offline I added new item. When connection appeared, item I added was synced with firebase database remotely. Unexpectedly onChildRemoved was called for the added value.. When I removed the operators orderByChild("someKey").equalTo(someValue), onChildRemoved wasn't trigerred. But onChildChanged was called instead.
Hope it will save someone a few hours.