0

I'm building an app and trying to fetch data immediately after the user presses a button. However, the data fetching part isn't happening until the onClickListener's onComplete is over. I need access to the data before onComplete is over.

Right now, my fragment contains an e-mail and password field, which they fill up and press "Login" button. I have set an onClickListener on the Login button inside which FirebaseAuth's signInWithEmailandPassword method is called. This returns an AuthResult Task, which I set an onCompleteListener to.

If this Task is successful, I trigger an addListenerForSingleValueEvent() task to fetch the relevant user's data from Firebase realtime database. It seems that this task runs after the onComplete method. How do I change that? I tried using AsyncTask for this but the result was the same.

my LoginFragment:

mLogInButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(final View v) {
            String email = mEmailText.getText().toString();
            String password = mPasswordText.getText().toString();
            final boolean[] loginSuccess = {false};

            Task task = mViewModel.signIn(email, 
password).addOnCompleteListener
                    (new OnCompleteListener() {
                @Override
                public void onComplete(@NonNull Task task) {
                    if (task.isSuccessful()) {
                        Log.d(TAG, "signInWithEmail:success");
                        Toast.makeText(getActivity(),
                                "Signed in!", 
Toast.LENGTH_SHORT).show();
                        loginSuccess[0] = true;
                        String uid = mViewModel.getAuth().getUid();
                        mViewModel.fetchUser(uid);
                        Log.d(TAG, mViewModel.getUser().getName());

                    } else {
                        Log.w(TAG, "signInWithEmail:failure", 
task.getException());
                        Toast.makeText(getActivity(), "Auth failed. 
Check log!",
                                Toast.LENGTH_SHORT).show();
                        loginSuccess[0] = false;
                    }
                }
            });
            //Task fetchDatabase = task.addOnCompleteListener(new 
//OnCompleteListener() {
                //@Override
                //public void onComplete(@NonNull Task task) {
                //    Log.d(TAG, "After!");
              //  }
            //}); 
//                updateUIWithDatabaseDetails(mView);
        }
    });

my fetchUser(uid) and its associated ValueEventListener:

public void fetchUser(String uid) {
    mUserDatabase.child(uid).addListenerForSingleValueEvent(userDataValueListener);
}

public ValueEventListener userDataValueListener = new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
        if (dataSnapshot.exists()) {
            Log.d(TAG, "User exists in remote!");
            mUser = dataSnapshot.getValue(User.class);
        }
        else {
            Log.d(TAG, "user doesn't exist.");
            mUser = null;
        }
    }

    @Override
    public void onCancelled(@NonNull DatabaseError databaseError) {
        Log.d(TAG, "Can't fetch Firebase Database user:" + databaseError.getMessage());
        mUser = null;
    }
};

The current setup immediately crashes with a NullPointerException at Log.d(TAG, mViewModel.getUser().getName()); because the database hasn't fetched the user and updated the user in the SharedViewModel yet.

Bottom line: I think the database fetch task gets queued to execute after onComplete on Login's onClick is over. Is there any way I can squeeze in this task while onComplete is running? I was thinking of using Handler to move it from the UI thread but I don't really understand it and really need some pointers for this.

Any help would be appreciated!

  • i think you have to add a callback to your `mViewModel.fetchUser(uid);` once user datat is fetched From Network and stored then this callback should trigger further call's such as `mViewModel.getUser().getName()` as currently you are making sync call's and when you call `getUser` in database that must be `null`. – Anmol Dec 23 '18 at 10:25
  • @Anmol I'm not sure how I'd implement a callback there. Could you give me some more pointers? – Ahmed Ishtiaque Dec 23 '18 at 10:43
  • `public void onDataChange(@NonNull DataSnapshot dataSnapshot) ` can you tell me when is this getting called in ur code? while the data fetch is completed ? – Anmol Dec 23 '18 at 10:47
  • Data is loaded from the Firebase Database asynchronously (and off the main thread). So when you call `mViewModel.fetchUser(uid)`, the Firebase client goes off to retrieve that data, and your other code continues. Once the data has been loaded, Firebase calls your `onDataChange` on the main/UI thread, so that you can process the results. Any code that needs this data will need to be inside `onDataChange`, or you will have to pass in a custom callback. For more on this see: https://stackoverflow.com/a/50435519 and https://stackoverflow.com/a/33204705 – Frank van Puffelen Dec 23 '18 at 15:27

0 Answers0