0

In my RegistrationActivity.java class file I've declared a numOfUsers variable to count the number of users in my database.


import ...

public class RegistrationActivity extends AppCompatActivity {

   //Other declarations
    private static long numOfUsers;

I've created an event listener for checking if a particular user exists in the database. Inside this event listener there is another event listener which counts the total number of users in the database.

        ValueEventListener eventListener = new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                if(!dataSnapshot.exists()) {
                    //create new user
                    database.addListenerForSingleValueEvent(new ValueEventListener() {
                        @Override
                        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                            if (dataSnapshot.exists()) {
                                numOfUsers = dataSnapshot.getChildrenCount();
                               


                            }


                        }
                      
                        @Override
                        public void onCancelled(@NonNull DatabaseError databaseError) {
                        }
                    });
//Displaying the numOfUsers in app
                    userinfo.setText(String.valueOf(numOfUsers));



                }

This prints 0. If I place userinfo.setText(String.valueOf(numOfUsers)); inside the second event listener then everything works fine.

        ValueEventListener eventListener = new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                if(!dataSnapshot.exists()) {
                    //create new user
                    database.addListenerForSingleValueEvent(new ValueEventListener() {
                        @Override
                        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                            if (dataSnapshot.exists()) {
                                numOfUsers = dataSnapshot.getChildrenCount();
//This works perfectly fine
                                userinfo.setText(String.valueOf(numOfUsers));
                               


                            }


                        }
                      
                        @Override
                        public void onCancelled(@NonNull DatabaseError databaseError) {
                        }
                    });

                  



                }

I don't understand why this is happening. numOfUsers is a static class variable I should be able to access it's value from anywhere inside the class. Is there a way I can't print numOfUsers outside the second event listener?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807

1 Answers1

0

It's not about where you access the data, but about when you access it.

Data is loaded from Firebase (and most modern cloud APIs) asynchronously, since it may take some time to get back.

Instead of blocking your main thread (which would lock out the user), your main code actually continues to run while the data is being loaded. Then when the data is available, your onDataChange gets called with it.

This means that in your code the userinfo.setText(String.valueOf(numOfUsers)) outside of onDataChange runs before onDataChange ever executes and thus passes the wrong value to the text view.

The solution for this is always the same: any code that needs the data from the database must be inside the onDataChange, or be called from there.

This is an incredibly common source of confusion, so I recommend reading more on:

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thanks for your answer! I went through the links you provided and I was wondering if there is a way to return the data back to the main function for further manipulation . In one the answers , you used callbacks as an interface to get the data out of the event listener but then it was just printed using a Toast. –  Sep 17 '21 at 05:52
  • A callback is the most common way to ensure your code that needs the value runs *after* the value has been read. In your callback you can do whatever you want with the data, so showing it in a toast is just one option. If you *have* the callback and toast working, but are having a hard time them implementing your use-case inside that callback, it might be a good idea to post a new question with a new [MCVE](http://stackoverflow.com/help/mcve) for that problem. – Frank van Puffelen Sep 17 '21 at 14:47