Firebase retrieve data Null outside method
I put the below method inside an onCreate()
, when the app launch it will trigger this method. But when I try to to get mName
outside the ValueEventListener()
, it return null
and the app crashed.
Anyone know why this issue happen?
ValueEventListener postListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
UserDetails info = dataSnapshot.getValue(UserDetails.class);
Log.d(TAG, String.valueOf(dataSnapshot));
mName = info.getName();
**Log.d(TAG, mName); // HERE GET THE NAME**
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "getUser:onCancelled", databaseError.toException());
}
};
mDatabase.child("Admin")
.child("Info")
.child(uid)
.addValueEventListener(postListener);
**Log.d(TAG, mName); // HERE GET NULL**
Solution 1:
As the others have said: all data from the Firebase Database is read asynchronously. It's easiest to see this by adding some more log statements to your code:
Log.d(TAG, "Before attaching listener");
mDatabase.child("Admin")
.child("Info")
.child(uid)
.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.d(TAG, "In listener's onDataChange");
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "getUser:onCancelled", databaseError.toException());
}
});
Log.d(TAG, "After attaching listener");
Unlike what you may expect, the output of this is:
Before attaching listener
After attaching listener
Inside listener's onDataChange
The way to deal with the asynchronous behavior is to reframe your problem.
Right now your code is written: first we load the data, then we log it.
If you reframe the problem to: "whenever the data is loaded, we log it", you get code like this:
mDatabase.child("Admin")
.child("Info")
.child(uid)
.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
UserDetails info = dataSnapshot.getValue(UserDetails.class);
Log.d(TAG, String.valueOf(dataSnapshot));
mName = info.getName();
Log.d(TAG, mName);
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "getUser:onCancelled", databaseError.toException());
}
});
So all code that needs the data, needs to be inside the onDataChange()
. Alternatively, you can make your own custom callback method and invoke that from onDataChange()
. But the logic is always the same: the code that needs toe data, is triggered from onDataChange()
when the data is available.
Solution 2:
That's because Firebase still didn't retrieve the data yet at the time you do:
Log.d(TAG, mName);
mName
is currently null since this line was executed before Firebase got the data.
To solve this, you have to Log.d(TAG, mName)
inside the onDataChange
method. Otherwise, it will be null
as you haven't set the value yet (since Firebase still didn't get the value yet).