FoxDonut
FoxDonut

Reputation: 466

Android - Transaction - Task is not yet complete

I found a few examples of "task not yet complete," but have not found any examples for transactions. I am using a transaction because in my application I need the operation to be able to fail if there is no internet connection. I can detect this with a transaction.

I have a Collection with Documents. I am trying to obtain the names of the documents. Sometimes the code works fine, but majority of the time I get the "task not yet complete" error. The frustrating thing is that I have a callback for "onComplete" so it's weird that the transaction isn't complete when the callback is... called.

I get the "task not yet complete exception in the onCompleteListener(). What's frustrating is that I even check to ensure if (task.isSuccessful() && task.isComplete()). Do I need to use a continuation? If so, please provide an example - I just don't quite understand it yet.

// Note: states is an ArrayList<String>
//       snapshot is a QuerySnapshot

public void getStatesList(){

    states.clear(); 
    states.add("Select A State");

    db.runTransaction(new Transaction.Function<Void>() {
        @Nullable
        @Override
        public Void apply(@NonNull Transaction transaction) {
            // Collect Snapshot data
            snapshot = db.collection("DATA").get();
            return null;
        }
    }).addOnCompleteListener(new OnCompleteListener<Void>() {
        @Override
        public void onComplete(@NonNull Task<Void> task) {

            if(task.isSuccessful() && task.isComplete()){

                try{
                    for(QueryDocumentSnapshot document : snapshot.getResult()){
                        states.add(document.getId());
                    }
                    sendResponseToActivity("Success", RESULT_OK);
                } catch (Exception e){
                    e.printStackTrace(); // Transaction is not yet complete
                    sendResponseToActivity("Fail", RESULT_OK);
                }
            }
        }
    }).addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception e) {
            if(e.getMessage().contains("UNAVAILABLE"))
                sendResponseToActivity("NoInternet", RESULT_OK);
            else
                sendResponseToActivity("Fail", RESULT_OK);
        }
    });

} // End getStatesList()

Upvotes: 3

Views: 1584

Answers (1)

samthecodingman
samthecodingman

Reputation: 26171

In your code, you call db.collection("DATA").get() but this doesn't operate inside of the transaction (that is done using transaction.get(docRef), transaction.update(docRef, newData) and so on). Just because the Task of the "transaction" has completed, it doesn't mean that this rogue database call has.

If the purpose is to get the server's copy of /DATA and only the server's copy, use Query#get(Source source) with Source.SERVER. (Note: pending server writes may be merged into the data to reflect the most up-to-date copy of the server's data)

public void getStatesList(){
    states.clear();
    states.add("Select A State");

    db.collection("DATA").get(Source.SERVER)
        .addOnSuccessListener(querySnapshot -> {
            for(QueryDocumentSnapshot document : querySnapshot){
                states.add(document.getId());
            }
            sendResponseToActivity("Success", RESULT_OK);
        })
        .addOnFailureListener(ex -> {
            if (ex.getMessage().contains("UNAVAILABLE")) {
                sendResponseToActivity("NoInternet", RESULT_OK); // RESULT_OK?
            } else {
                sendResponseToActivity("Fail", RESULT_OK); // RESULT_OK?
            }
        });
}

However, because the above version uses and modifies global variables, I would implement it using:

/** Returns server's list of states */
public Task<ArrayList<String>> getStatesList() {
    return db.collection("DATA").get(Source.SERVER)
        .onSuccessTask(querySnapshot -> {
            ArrayList<String> states = new ArrayList<>();

            // NOTE: states.add("Select A State"); was removed here
            
            for (QueryDocumentSnapshot document : querySnapshot) {
                states.add(document.getId());
            }

            return Tasks.forResult(states);
        });
}

Then use it like so:

getStatesList().addOnCompleteListener(task -> {
    if (task.isSuccessful()) {
        ArrayList<String> states = task.getResult();
        // do something with states, like update UI
        // don't forget that "Select A State" isn't in the array
    } else {
        Exception ex = task.getException();
        if (ex.getMessage().contains("UNAVAILABLE")) {
            // offline, do something
        } else {
            // unexpected error, do something
        }
    }
})

Upvotes: 2

Related Questions