I am currently working on an Android project with Firebase integration however I am struggling with retrieving a class object from Firebase Realtime database because of Firebase listeners.
Let me elaborate on my project so that you can get the main idea of it. I use MVVM architecture and I have 2 Activities which one of them is for authentication while other is for main usage.
First activity contains Register, Login, Password Reset fragments Second activity contains 4 Fragments which are for main functions of the program
Fragments in the first activity use a shared viewmodel "AuthViewModel" which contains methods invoking AuthRepository object's methods and LiveData variables providing data to Fragments
And all of my Firebase Auth operations are handled in this AuthRepository's methods, For instance this is the register method in AuthRepository, which use FireBase Auth methods to register a user meanwhile adding User into realtime database by creating a User object.
public void register(String email , String pass, String username, String department){
auth.createUserWithEmailAndPassword(email , pass).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()){
firebaseUserMutableLiveData.postValue(auth.getCurrentUser());
// Add user to realtime database
FirebaseUser user = auth.getCurrentUser();
dbRef.child("Users").child(user.getUid()).setValue(new User(email, username, department));
}
else{
Toast.makeText(application, task.getException().getMessage(), Toast.LENGTH_SHORT).show();
}
}
});
}
Since you got the main idea, let's move on to my problem. Just like the AuthRepository, I have a MainRepository to be added as a variable in MainViewModel which is the ViewModel responsible for 4 fragments in the second activity.
Whatever I have tried, I could not retrieve the User object of the corresponding user and assign this object to the user variable in MainRepository class.
The problem is not about retrieving the data because "IN LISTENER: " Toast displays the correct User object in the Realtime database
However "OUT LISTENER: " and "FINAL: " Toast show the User object initialized out of the listener. I don't know why but the user object in the listener seems to be a copy of the user object in the class even though they share the same hashcode if I don't assign user object with the user object in firebase.
Despite of a long search I couldn't access the "real" object "not the copy" of outer class from inner class. Even I seem to be accessing and invoking methods of the outer object they do not persist out of the listener.
What is the problem here, what can I do to solve it? And if it is not possible to solve, what should I do instead?
Different Output
Toast.makeText(application, "IN LISTENER: "+ user.hashCode() + user.getDepartment(), Toast.LENGTH_SHORT).show();
Same Output
Toast.makeText(application, "OUT LISTENER: "+ user.hashCode() + user.getDepartment(), Toast.LENGTH_SHORT).show();
Toast.makeText(application, "FINAL: "+ user.hashCode() + user.getDepartment(), Toast.LENGTH_SHORT).show();
public class MainRepository {
private Application application;
private FirebaseAuth auth;
private DatabaseReference dbRef;
private MutableLiveData<User> userMutableLiveData;
private User user;
public MainRepository(Application application) {
this.application = application;
auth = FirebaseAuth.getInstance();
dbRef = FirebaseDatabase.getInstance().getReference();
getUser();
Toast.makeText(application, "FINAL: " + user.hashCode() + user.getDepartment(), Toast.LENGTH_SHORT).show();
}
class ListenerInner implements ValueEventListener {
MainRepository mainRepository;
ListenerInner(MainRepository mainRepository) {
this.mainRepository = mainRepository;
}
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
mainRepository.setUser(snapshot.getValue(User.class));
Toast.makeText(application, "IN LISTENER: "+ user.hashCode() + user.getDepartment(), Toast.LENGTH_SHORT).show();
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
Toast.makeText(application, "Error: " + error.getMessage(), Toast.LENGTH_SHORT).show();
}
}
public void getUser() {
setUser(new User("aaa", "bbb", "ccc"));
dbRef.child("Users").child(auth.getCurrentUser().getUid()).addListenerForSingleValueEvent(new ListenerInner(this));
Toast.makeText(application, "OUT LISTENER: " + user.hashCode() + user.getDepartment(), Toast.LENGTH_SHORT).show();
}
private void setUser(User user) {
this.user = user;
}
}