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?
Related
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) {
}
});
I have recently added a chat feature to my app.
I am storing the messages and their timestamps in Firebase database.
I am trying to figure out a way of displaying (in the current chat room) only the last (let's say) 60 messages. This would mean retrieving only the last 60 items of the current conversation from Firebase using limitToLast().
Furthermore I would like to add a Load more button that would appear only when the 60 messages limit has been reached by swiping up and it should load another 60.
Is there a proper way of handling the message archive as I stated above?
Using the code below I actually retrieve the whole specific message archive. I find this ineffective when the specific chat archive has thousands of messages.
mChildEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
ChatMessage chatMessage = dataSnapshot.getValue(ChatMessage.class);
mMessageAdapter.add(chatMessage);
}
#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(DatabaseError databaseError) {
}
};
mDatabaseReference.child("chat_messages").child(chatId).addChildEventListener(mChildEventListener);
To achieve this, I recomand you using the following logic:
pageEndOffset = 0;
pageLimit = 60;
pageEndOffset += pageLimit;
Query query = mDatabaseReference.child("chat_messages")
.orderByChild("chatId").limitToFirst(pageLimit)
.startAt(pageEndOffset);
query.addValueEventListener(YourActivity.this);
Hope it helps.
I am building a social network like Instagram. I have worked on many social networks before, using mySQL. I am new to firebase server.
I want to search users via name and want to show the follow/following button on the list view. But I am little confused to run android firebase queries in a standard format. I do not want to run unnecessary loops. Below is my database structure for the follow table.
Node name is follow and follower_id refers to the user who is following the user and user who gets followed is referred as followed_id.
How to write a simple query using android firebase to show all the users with the name starting (e.g "an") and with the status that I am already following him/her or not.
FYI: I am not using firebase rest API's.
How to write a simple query using android firebase to show all the users with the name starting (e.g "an") and with the status that I am already following him/her or not.
I think you can't do it in a single query.
You could try to do it with nested queries, something like this:
ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
// Retrieve the userId
User user = dataSnapshot.getValue(User.class);
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot usersFollowedDataSnapshot) {
for ( DataSnapshot userFollowedDataSnapshot : usersFollowedDataSnapshot.getChildren() ) {
// Retrieve the follower structure
Follow follow = userFollowedDataSnapshot.getValue(Follow.class);
if ( myUserId == follow.getFollowerId() ) {
// This is a user that I'm following and which name starts with "am"
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
Query secondQuery = mFirebaseDatabaseReference.child("follow").orderByChild("followedId").equalTo(user.getId());
secondQuery.addListenerForSingleValueEvent(valueEventListener);
}
#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(DatabaseError databaseError) {
}
};
Query firstQuery = mFirebaseDatabaseReference.child("users").orderByChild("name").startAt("am").endAt("am\uf8ff");
firstQuery.addChildEventListener(childEventListener);
I know it's not very straightforward and looks disapponting. It could be also slow, but it worth trying.
Alternatively, you should consider to structure your database in a way to simplify the firebase queries.
See more on firebase queries for example here and here.
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.
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.