1

I am new to doing asynchronous programming in Android Java. I am wondering if there is a way to run another Callback after an initial Callback function has completed. Right now, I think they are running in parallel even though the second relies on the first.

First Callback:

// GETTING USER
    private interface FirestoreUserCallback {
        void onCallback (User myUser);
    }

    private void getUser(final FirestoreUserCallback firestoreCallback) {
        Task<DocumentSnapshot> task = fStore.collection("users").document(fAuth.getCurrentUser().getUid()).get();
        task.addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
            @Override
            public void onSuccess(DocumentSnapshot documentSnapshot) {
                user = documentSnapshot.toObject(User.class);
                firestoreCallback.onCallback(user);
                Log.d(TAG, "user created");
            }
        });
        task.addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                Log.d(TAG, "user creation failed");
            }
        });
    }

Second Callback:

// GETTING ALL DOCUMENTS
    private interface FirestoreDocumentCallback {
        void onCallback (List<TableEntries> myEntries);
    }

    private void getDocuments (final FirestoreDocumentCallback firestoreDocumentCallback) {
        fStore.collection("result")
                .document(Integer.toString(user.getCompanyNumber())) // need to use User object returned from the first Callback
                .collection("SAM").get()
                .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                    @Override
                    public void onComplete(@NonNull Task<QuerySnapshot> task) {
                        List<TableEntries> results = new ArrayList<>();
                        if (task.isSuccessful()) {
                            for (QueryDocumentSnapshot document : task.getResult()) {
                                // add objects to results ArrayList ...
                                Log.d(TAG, document.getId() + " => " + document.getData());
                            }
                            firestoreDocumentCallback.onCallback(results);
                        } else {
                            Log.d(TAG, "Error getting documents: ", task.getException());
                        }
                    }
                });
    }

onCreate:

getUser(new FirestoreUserCallback () {
    @Override
    public void onCallback(User myUser) {
        user = myUser;
    }
});

getDocuments(new FirestoreDocumentCallback() {
    @Override
    public void onCallback(List<TableEntries> myEntries) {
        entries = myEntries;
    }
});

getDocuments() relies on the user variable being given its value from the first Callback. I'm receiving this error:

java.lang.NullPointerException: Attempt to invoke virtual method 'double java.lang.Double.doubleValue()' on a null object reference
Ayren King
  • 417
  • 8
  • 16
  • 1
    When you're chaining callbacks, first thing to keep in mind is that you're about to end up with 'callback hell' situation. That's off topic but answer to your question is if you're depending on your first operation to finish to start second one then you should write second operation inside callback of first operation. – Jeel Vankhede Nov 27 '20 at 06:21
  • 1
    In your case, you should be calling `getDocuments` method from overridden method `onCallback` of `getUsers` where you receive user object back from callback. – Jeel Vankhede Nov 27 '20 at 06:22
  • Thanks for the answer! Essentially, I should move the ```getDocuments()``` underneath ```user = myUser```? – Ayren King Nov 27 '20 at 06:27
  • 1
    Yes that will fix your issue, check it out – Jeel Vankhede Nov 27 '20 at 06:35

3 Answers3

0

Callbacks are looking fine. You just need to check if your value is null or not before accessing it. Just add a null check

 if(doubleValue!=null)
Priyanka
  • 1,791
  • 1
  • 7
  • 12
0

Using RxJava. First, we fetch the user and then fetch the documents. Rx-Java has an operator flatmap. flatmap is used to execute the sequential tasks, where the second task is dependent on the data from the first task.

    final CompositeDisposable disposable = new CompositeDisposable();
//function to fetch user data
Single<User> getUser(){
    return API.getUserData(...);
}
//function to fetch ducuments
Sinlge<UserDetail> getDocuments(int userId){
    return API.getUserDetail(userId, ...);
}
//Subscribe
disposable.add(getUser()
    .flatmap(user-> return getDocuments(...))
    .subscribeOn(Scheduler.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribeWith(new DisposableSingleObservable(){
              @Override
              public void onSuccess(UserDetail userDetail){
                  Log.v("Api result", "Successful";
                  //Do some work 
              }
              @Override
              public void onError(Throwable e)
                  Log.v("Api result", "Error Returned");
              }
     }));

If either of the API call fails, onError() is called. If first API fails, second API call is not executed and onError() is called.

Amirhosein
  • 4,266
  • 4
  • 22
  • 35
0

The simplest solution for your use-case is to pass both queries to Tasks.whenAllSuccess() method, as explained in my answer from the following post:

So once the task is complete, you can use the elements from both queries. Another solution might be to use Android Jetpack with LiveData along with ViewModel, as the Android team recommends.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193