2

In my application I'm able to get children count by datasnapshot.getchildren(). But what should I do if I want to store that count in a variable?

mRefReg.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            Log.i(dataSnapshot.getKey(),dataSnapshot.getChildrenCount()+"Count");

        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    });

including "mCount = dataSnapshot.getChildren" does not seem to work.

Parag Pawar
  • 827
  • 3
  • 12
  • 23
  • why is "mCount =..." not working ? If mCount is variable defined outside of your anonymous class it should work. What is the error message of your identity ? – Oliver Dec 15 '16 at 11:58
  • Firebase has recently release Cloud Functions. Have a look at this [answer](http://stackoverflow.com/a/42713792/5861618) for more details – Rosário Pereira Fernandes Mar 10 '17 at 08:54

4 Answers4

7

What you're seeing is likely the fact that data from Firebase is loaded asynchronously. The first time you encounter this, it's a big paradigm shift. But since most modern cloud APIs work this way, it's best to embrace it as quickly as possible.

The easiest way I've found to see what happens when we place some logging statements:

System.out.println("Before adding listener");
mRefReg.addValueEventListener(new ValueEventListener() {
    public void onDataChange(DataSnapshot dataSnapshot) {
        System.out.println("In onDataChange");
    }
    public void onCancelled(DatabaseError databaseError) { }
});
System.out.println("After adding listener");

Contrary to what your intuition tells you, the output of this will be:

Before adding listener

After adding listener

In onDataChange

The reason for this is (as I said at the start) that Firebase asynchronously gets the data from the database. A fun way to realize why that is, is to make a change to your data (for example in your Firebase Database console). When you make this change, the onDataChange will be invoked again and print another:

In onDataChange

Unfortunately this means that you cannot do the following to handle the children count:

int count = -1;
System.out.println("Before adding listener, count="+count);
mRefReg.addValueEventListener(new ValueEventListener() {
    public void onDataChange(DataSnapshot dataSnapshot) {
        count = dataSnapshot.getChildrenCount();
        System.out.println("In onDataChange, count="+count);
    }
    public void onCancelled(DatabaseError databaseError) { }
});
System.out.println("After adding listener, count="+count);

Because the output will be:

Before adding listener: count=-1

After adding listener: count=-1

In onDataChange: count=3 (assuming you have 3 children)

The best trick I've found to deal with this asynchronous loading is to reframe the problem. Instead of saying "first we get the count, then we do xyz with it", try framing it as "whenever we get the count, do xyz with it". This means that you need to move the code that requires the count into onDataChange():

mRefReg.addValueEventListener(new ValueEventListener() {
    public void onDataChange(DataSnapshot dataSnapshot) {
        int count = dataSnapshot.getChildrenCount();
        // TODO: show the count in the UI
    }
    public void onCancelled(DatabaseError databaseError) { }
});

If you implement it like this, the count in the UI will be updated whenever the number of items in the database changes.

Community
  • 1
  • 1
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Are you sure if there is any method like getNumChildren()? Becuase it's giving me error: "cannot resolve method getNumChildren()"! – Parag Pawar Dec 15 '16 at 17:17
  • Anyway I've got another method working. I just used "long count = dataSnapshot.getChildrenCount()" instead on integer and it worked! – Parag Pawar Dec 15 '16 at 17:55
  • Thanks for pointing out the faulty method name. I fixed the code in my answer. – Frank van Puffelen Dec 15 '16 at 18:19
  • Yeah, and thanks for helping. I've one more doubt, how can i access Children count when my application is not connected to network via using firebase offline capabilities? – Parag Pawar Dec 16 '16 at 10:36
  • It depends: transient loss of connectivity is handled automatically. For longer-term lack of connectivity, you can enable disk persistence. See https://firebase.google.com/docs/database/android/offline-capabilities – Frank van Puffelen Dec 16 '16 at 15:16
  • with this, do not we have downloaded all the datasanpshot with this technique? does not that mean a great deal of data? if yes, are there an a fast way. – mehmet Mar 29 '18 at 08:05
  • This does NOT work with huge amount of data. It just gets slower and slower and finally times out. Tried with 500k+ records. – kkazakov May 30 '18 at 05:43
  • @kkazakov Downloading all child data to get the count indeed becomes prohibitive as you get more child nodes. For a scalable solution, keep a separate count node as shown here https://stackoverflow.com/questions/15148803/in-firebase-is-there-a-way-to-get-the-number-of-children-of-a-node-without-load and https://github.com/firebase/functions-samples/tree/master/child-count. – Frank van Puffelen May 30 '18 at 13:37
  • @FrankvanPuffelen that's exactly what I'm doing ;) – kkazakov May 31 '18 at 16:17
2

There's some info missing in your question, but I think I know what's going on. Listeners in Firebase are asynchronous, so you cannot set a variable that easy.

Options to go:

1) Declare your variable inside the onDataChange method.

2) Only use the variable when onDataChange method has completed.

3) Pass a callback function in the method where you have declared your listener.

1
long ct = 0;    

public void Count() {
            databaseReference.child("a").child("b").addValueEventListener(new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {
                    if (dataSnapshot.exists()) {
                        ct = dataSnapshot.getChildrenCount()`enter code here`;
                    } 
                }

                @Override
                public void onCancelled(DatabaseError databaseError) {

                }
            });

        }

Call this function at the beginning and it will keep updating the variable ct whenever the child count increase or decreases in the firebase database. Hope it helps

Akshay Sahai
  • 2,121
  • 1
  • 17
  • 20
0

Just do this. The function datasnapshot.getChildrenCount returns long type value so you need to cast it to integer first.

    mRefReg.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                mCount = (int) dataSnapshot.getChildrenCount;
                Log.i(dataSnapshot.getKey(), mCount +""`enter code here`);

            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
Pavel Santaev
  • 845
  • 7
  • 17