0

I'm trying to get a summation of a particular column within a table in my Firebase database to produce an average (to contrast with a current reading) of a value in my Android application. Just as a test, I wanted to show that sum in a TextView in my test application. When I first declare the addValueEventListener:

 reff.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot snapshot) {


            for (DataSnapshot snapshot2:  snapshot.getChildren()){
                sum = sum + snapshot2.child("age").getValue(Integer.class);
            }
        }

        @Override
        public void onCancelled(@NonNull DatabaseError error) {

        }
    });

And then make a database setValue() call and set the textView in an onClickListener, this works as expected.

 button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            reff.child(String.valueOf(maxid+1)).setValue(member);
            Toast.makeText(MainActivity.this, "data inserted successfully", Toast.LENGTH_LONG).show();
            a.setText(String.valueOf(sum));

        }
    });

Ultimately in my application, however, I don't want the "sum" value triggered by a button click. I want this to be present so that I can compare new data with a rolling average from my database. If I move the above code from the OnClickListener into my main onCreate method, the value for sum stays at 0. Reading various threads, I also substituted addValueEventListener() for addListenerForSingleValueEvent() but this had no impact.

Would anyone be able to suggest a way I could reformat this without having to rely on a button being pushed to get the summation from my table?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
wnjl
  • 162
  • 1
  • 10

1 Answers1

1

The call to a.setText() needs to be inside onDataChange, as that is the only code that is guaranteed to be executed when the updated data comes from the database.

So:

reff.addValueEventListener(new ValueEventListener() {

    @Override
    public void onDataChange(@NonNull DataSnapshot snapshot) {
        for (DataSnapshot snapshot2:  snapshot.getChildren()){
            sum = sum + snapshot2.child("age").getValue(Integer.class);
        }
        a.setText(String.valueOf(sum));
    }

    @Override
    public void onCancelled(@NonNull DatabaseError error) {
        throw error.toException();
    }
});

Also: please never leave onCancelled empty, as your ignoring potential errors that way,.

Also see: getContactsFromFirebase() method return an empty list, which I'll close your question as a dupe of - I just wanted to show you the relevant solution in code too.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • This was tremendously helpful, thank you! Just, as a follow up, not that my initial code was extremely insightful, but I also wanted to try to use a counter object ('maxid' in my example) to identify each database entry as opposed to the randomly generated Firebase value for each database entry. Being able to pass the numeric value of getChildrenCount() outside onDataChange() is the issue. I also read your answer in a separate thread on this very subject. If I try to make a change to the database inside onDataChange() with the accurate getChildrenCount(), an infinite loop occurs. – wnjl Oct 22 '20 at 01:00
  • Yeah, the most important thing is that any code that uses the data from the database must be inside `onDataChange` or be called from there. The scope (where the code that uses the variable is) doesn't matter that much, but the timing will only be correct if you trigger it from `onDataChange`. – Frank van Puffelen Oct 22 '20 at 02:00