8

I'm trying to setup an Android Service in my app that listens for new Childs in a Firebase Ref, and throws a Notification when that happens.

I'm having issues because addChildEventListener onChildAdded apparently is called one time for every existent record and only then actually listens for new childs..

In this answer @kato states that if addChildEventListener is called like ref.endAt().limit(1).addChildEventListener(...) it would get only the newly added records.

It actually only gets one record at a time (I suppose with limit(1)) but it still gets an existant record before listening for added records.

Here's some code:

Initializing the Listener in onCreate():

@Override
public void onCreate() {
    super.onCreate();
    this.handler = new ChildEventListener() {
        @Override
        public void onChildAdded(DataSnapshot dataSnapshot, String s) {
            AllowedGroup ag = dataSnapshot.getValue(AllowedGroup.class);

            postNotif("Group Added!", ag.getName());
        }
        ...rest of needed overrides, not used...

I'm using the AllowedGroup.class to store the records, and postNotif to build and post the notification. This part is working as intended.

Then, onStartCommand():

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    this.f = new Firebase(FIREBASE_URL).child("users").child(this.currentUserUid).child("allowedGroups");
    f.endAt().limit(1).addChildEventListener(handler);
    return START_STICKY;
}

It still returns one existant record before actually listening for newly added childs.

I've also tried querying by timestamp, like so:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    this.f = new Firebase(FIREBASE_URL).child("users").child(this.currentUserUid).child("allowedGroups");
    f.startAt(System.currentTimeMillis()).addChildEventListener(handler);
    return START_STICKY;
}

Hoping that it would only get records set after the service was started. It doesn't get existant records, but doesn't even get newly added childs.

EDIT:

I've also thought of something like getting into memory first all of the existant records, and conditionally post a notification if the record brought by onChildAdded does not exist on the previously gathered list, but that seems a bit like overkill, and thought that could be an easier (more API-friendly) way of doing this, am I right ?

Can anyone provide me with some insight on this ? I can't really find anything on the official docs or in any StackOverflow question or tutorial.

Thanks.

Community
  • 1
  • 1
jsfrocha
  • 1,812
  • 2
  • 21
  • 32
  • 1
    possible duplicate of [How to retreive only new data?](http://stackoverflow.com/questions/18270995/how-to-retreive-only-new-data) – Kato Jul 14 '14 at 14:55
  • what about `firebaseRef.on('child_added', function(childSnapshot, prevChildName) { // code to handle new child. });` ? – Alberto Malagoli Jul 17 '14 at 12:55
  • Have you tried spawning a new thread in the service, then adding the listener? Have you tried ordering by a modifiedOn field in your groups? – prodaea May 26 '15 at 15:57
  • are you found a way ti fix this problem ? – Amine Harbaoui Dec 12 '16 at 05:16
  • You can store your values using the default key by firebase(using push() method). Each key auto-generated by firebase is placed at the bottom of the list, so after child added has benn fired, just check the final value in the list and it will be your new data. – Gordon developer Feb 17 '18 at 08:30

3 Answers3

1

If you have a field modifiedOn, you can index that and setup a query like:

Query byModified = firebaseEndpoint.orderByChild("modifiedOn").startAt(lastInAppTime);
prodaea
  • 1,720
  • 1
  • 14
  • 20
0

For me the logic was is to have value -"status" for example- which needs to be validated before deciding whether it is really new or was an old record then I set the "status" to a different value so I don't get it next time:

@Override
        public void onChildAdded(DataSnapshot dataSnapshot, String previousChildKey) {

                if(dataSnapshot.hasChildren()) {

                    String Id = (String) dataSnapshot.child("user_id").getValue();
                    String status = (String) dataSnapshot.child("status").getValue();

                    if (Id != null && Id.equals(storedId) && status != null && status.equals("created")) {
                        Log.d("INCOMING_REQUEST", "This is for you!");
                        sessionsRef.child(dataSnapshot.getKey()).child("status").setValue("received");
                    }

                }


        }
mmahgoub
  • 3,910
  • 3
  • 20
  • 19
  • What happens if multiple account access to this data, one account change "status" => "received" then the rest accounts couldn't get these data anymore. – Khang Tran Mar 13 '17 at 22:42
0

I overcome this problem by keeping items of firebase database reference node in a List; and whenever onChildAdded(...) method is triggered, check if the incoming datasnapshot is in your list or not; if not, then it's new data

In order to achieve this you must meet below conditions:

  1. Have a unique value that must not repeated from item to item. (this can be easily achieved when you add items into Firebase using .push() method.
  2. Override .equals() method of your model class in order to fulfill the comparison based on this unique value.

Here code snippets based on your inputs

Model class

public class AllowedGroup {

    private String id;
    public static List<AllowedGroup> sGroups;

    // reset of fields ...


    public AllowedGroup() {
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }


    @Override
    public boolean equals(@Nullable Object o) {

        // If the object is compared with itself then return true
        if (o == this) {
            return true;
        }

        // Check if o is an instance of AllowedGroup or not
        if (!(o instanceof AllowedGroup)) {
            return false;
        }

        // typecast o to AllowedGroup so that we can compare data members
        AllowedGroup group = (AllowedGroup) o;

        // Compare data based on the unique id
        return (group.id).equals(id);
    }

}

Listening to firebase added nodes

@Override
public void onCreate() {
    super.onCreate();
    this.handler = new ChildEventListener() {
        @Override
        public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {

            AllowedGroup group = dataSnapshot.getValue(AllowedGroup.class);

            if (!AllowedGroup.sGroups.contains(group)) {
                // Here you'll receive only the new added values
            }

        }

        // ...rest of needed overrides, not used... 
    }

}
Zain
  • 37,492
  • 7
  • 60
  • 84