Home > Software engineering >  How to modify objects of outer class from an inner class so as to retrieve class object from firebas
How to modify objects of outer class from an inner class so as to retrieve class object from firebas

Time:12-12

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;
}

}

CodePudding user response:

The problem is not where you access the data from the database, but when you access it.

Data is loaded from Firebase (and most cloud APIs) asynchronously, since it may take some time to get it. While the data is being loaded, you main code/thread continues to execute. And then when the code is available, the onDataChange on your listener is called with that data.

This means that any code on the main thread (such as Toast.makeText(application, "OUT LISTENER: " user.hashCode() user.getDepartment(), Toast.LENGTH_SHORT).show()) will run before the onDataChange has been called and has done its mainRepository.setUser(snapshot.getValue(User.class)). The easiest way to verify this is by setting breakpoints on these lines and running the code in a debugger, or by logging some output and checking the order of its output in logcat.

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

For a good example of this problem, and using these approaches, see:

  • Related