0

the maxid integer variable should have the value for the getChildrenCount() from the firebase database, but it seems that the addValueEventListener is not working.

The following is my java code from Android Studio:

@IgnoreExtraProperties
public class ProfileID_Generator extends AppCompatActivity {
    public int maxid;
    private int ProfileID;
    private String value;
    private final DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("User");

    public ProfileID_Generator(){}

    public int profileID_generate(){
        ref.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                if(snapshot.exists()){
                    maxid = (int) snapshot.getChildrenCount();
                }
            }
            @Override
            public void onCancelled(@NonNull DatabaseError error) {
                throw error.toException();
            }
        });

        ref.child(Integer.toString(maxid)).child("ProfileID").addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                if(snapshot.exists()){
                    String val = snapshot.getValue(String.class);
                    value = val;
                }
            }
            @Override
            public void onCancelled(@NonNull DatabaseError error) {
                throw error.toException();
            }
        });

        if(maxid==0){
            ProfileID = 10001;
        }
        else{
            ProfileID = Integer.parseInt(value)+1;
        }

        return ProfileID;
    }
}

The following is the data from the realtime database from firebase:

enter image description here

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • If you understand Kotlin, then this [resource](https://medium.com/firebase-tips-tricks/how-to-read-data-from-firebase-realtime-database-using-get-269ef3e179c5) will definitely help. – Alex Mamo Dec 04 '22 at 08:43

1 Answers1

0

All data is loaded from Firebase (and most cloud APIs) asynchronously, which changes the order in which code executes from what you may expect and be used to.

The problem is not that getChildrenCount isn't working, but that ref.child(Integer.toString(maxid)) is executed before maxid = (int) snapshot.getChildrenCount();. It's easiest to see this if you add some logging:

Log.i("Firebase", "Start loading maxid")
ref.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot snapshot) {
        if(snapshot.exists()){
            Log.i("Firebase", "Got data for maxid")
            maxid = (int) snapshot.getChildrenCount();
        }
    }
    @Override
    public void onCancelled(@NonNull DatabaseError error) {
        throw error.toException();
    }
});

Log.i("Firebase", "Start loading ProfileID")
ref.child(Integer.toString(maxid)).child("ProfileID").addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot snapshot) {
        if(snapshot.exists()){
            String val = snapshot.getValue(String.class);
            value = val;
        }
    }
    @Override
    public void onCancelled(@NonNull DatabaseError error) {
        throw error.toException();
    }
});

When you run this code it logs:

Start loading maxId
Start loading ProfileID
Got data for maxid

This may not be the order in which you expected the output, but it is working as designed and explains perfectly why your code doesn't work: by the time you starting loading the ProfileID, the maxid isn't known yet.


The solution for this type of problem is always the same: any code that needs the data from the database has to be inside onDataChange, be called from there, or otherwise synchronized.

So the simplest fix is to move the code that loads the ProfileID into the onDataChange that sets maxid:

ref.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot snapshot) {
        if(snapshot.exists()){
            maxid = (int) snapshot.getChildrenCount();

            ref.child(Integer.toString(maxid)).child("ProfileID").addValueEventListener(new ValueEventListener() {
                @Override
                public void onDataChange(@NonNull DataSnapshot snapshot) {
                    if(snapshot.exists()){
                        String val = snapshot.getValue(String.class);
                        value = val;
                    }
                }
                @Override
                public void onCancelled(@NonNull DatabaseError error) {
                    throw error.toException();
                }
            });
        }
    }
    @Override
    public void onCancelled(@NonNull DatabaseError error) {
        throw error.toException();
    }
});

This is an incredibly common problem and stumbling block for developers that are new to asynchronous APIs, so I recommend reading up on some more questions about it, such as:

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • it's still giving the same output as before. – mubtasim rakheen Dec 03 '22 at 19:46
  • like it is supposed to show profile id 0f 10002 but it is still showing profile id of 10001. Please Help!!! – mubtasim rakheen Dec 03 '22 at 19:58
  • Did the entire explanation that I gave of asynchronous behavior make sense? Does it also make sense why your code doesn't work based on that? While there may be more problems, I want to make sure you understand the explanation I gave. --- After that, please provide more useful debug information. E.g. If you set a breakpoint on each line of the code you shared, run the code in a debugger, and then check the value of each variable on each line, which is the **first** line that doesn't do what you expect it to do? – Frank van Puffelen Dec 03 '22 at 20:10