0

I'm trying to get a product from a document form the cloud firestore and then put that product in the shopping cart. When i read (successfully) the product, i try to put it in an arraylist that is declared outside but it doesnt work unless i put final to the variable. Doing so, when I run the code below, I successfully retrieve the data, but the operation carrelloAttuale.prodotti.add(prod) is executed after the command transaction.update(), so the update doesn't upload nothing different from the start.

//prendo l'utente
                FirebaseAuth auth= FirebaseAuth.getInstance();

                //mi salvo il codice del prodotto scannerizzato
                final String codiceProdottoScannerizzato=String.valueOf(intentData);
                final FirebaseFirestore db = FirebaseFirestore.getInstance();
                final DocumentReference docRef = db.collection("carrelli").document(auth.getUid());
                final DocumentReference docrefprodotti = db.collection("prodotti").document(codiceProdottoScannerizzato);
                db.runTransaction(new Transaction.Function<Void>() {
                    @Override
                    public Void apply(Transaction transaction) throws FirebaseFirestoreException {
                        DocumentSnapshot snapshot = transaction.get(docRef);
                        final Carrello carrelloAttuale = snapshot.toObject(Carrello.class);

                        docrefprodotti.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
                            @Override
                            public void onComplete(@NonNull Task<DocumentSnapshot> task) {
                                if (task.isSuccessful()) {
                                    DocumentSnapshot document = task.getResult();
                                    if (document.exists()) {
                                        Prodotti prod=document.toObject(Prodotti.class);
                                        prod.id=codiceProdottoScannerizzato;
                                        prod.totalePezziCarrello=1;
                                        carrelloAttuale.prodotti.add(prod);
                                        Log.d(TAG, "PRODOTTO: " + prod.toString());
                                    } else {
                                        Log.d(TAG, "No such document");

                                    }
                                } else {
                                    Log.d(TAG, "get failed with ", task.getException());
                                }
                            }
                        });

                        Log.d(TAG, "CARRELLO FB: " + carrelloAttuale.size());
                        transaction.update(docRef, "prodotti", carrelloAttuale.getProdotti());

                        // Success
                        return null;
                    }
                }).addOnSuccessListener(new OnSuccessListener<Void>() {
                    @Override
                    public void onSuccess(Void aVoid) {
                        Log.d(TAG, "Transaction success!");
                    }
                })
                        .addOnFailureListener(new OnFailureListener() {
                            @Override
                            public void onFailure(@NonNull Exception e) {
                                Log.w(TAG, "Transaction failure.", e);
                            }
                        });

I expect that the command update is executed after the carrelloAttuale.prodotti.add(prod) in the debug log the order of tags are: CARRELLO FB: 0 PRODOTTO: Nome: latte

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807

2 Answers2

1

Data is loaded from Firestore asynchronously, since it may have to be retrieved from the server. To prevent blocking the app, the main code continues while the data is being retrieved. Then when the data is available, your onComplete gets called.

This means that any code that needs the data from the data, must be inside the onComplete method, or be called from there. So something like:

docrefprodotti.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
    @Override
    public void onComplete(@NonNull Task<DocumentSnapshot> task) {
        if (task.isSuccessful()) {
            DocumentSnapshot document = task.getResult();
            if (document.exists()) {
                Prodotti prod=document.toObject(Prodotti.class);
                prod.id=codiceProdottoScannerizzato;
                prod.totalePezziCarrello=1;
                carrelloAttuale.prodotti.add(prod);
                Log.d(TAG, "PRODOTTO: " + prod.toString());
            } else {
                Log.d(TAG, "No such document");

            }
        } else {
            Log.d(TAG, "get failed with ", task.getException());
        }

        Log.d(TAG, "CARRELLO FB: " + carrelloAttuale.size());
        transaction.update(docRef, "prodotti", carrelloAttuale.getProdotti());
    }
});

Also see:

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • i tried your method but I have to declare final transaction, which causes crash when i runn the app. Thanks btw, i resolved calling inside the onComplete function a custom function that does the transaction – ilpeppone7 Jul 27 '19 at 15:05
  • I don't really understand what the "declare final transaction" means here. Can you show the code and the error message and stack trace you get? – Frank van Puffelen Jul 27 '19 at 22:43
0

"the command update" is executed before "carrelloAttuale.prodotti.add(prod)" is called because the onComplete() method has an asynchronous behaviour and returns immediately. This means that listener will not get invoked until some time later, after the database update operation is complete. There is no guarantee how long it will take. Depending on your connection speed and the state, it may take from a few hundred milliseconds to a few seconds for the update operation to complete.

If you want to use some logic with that data, you must wait until the asynchronous Firebase database operation is complete. This means that you can only use the prod object inside the listener callback itself.

For more informarions, I recommend you see the last part of my anwser from this post in which I have explained how it can be done using a custom callback. You can also take a look at this video for a better understanding.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • thank you for your help, now that i learned the behaviour of these methods i've successfully passed my data into a function that does what i want. Now i'm trying to figure out why the transaction fails, but this is another problem. – ilpeppone7 Jul 27 '19 at 15:07
  • Good to hear that it worked. Yes, it sounds as another problem that should basically be considered another question. So please post another fresh question using its own [MCVE](https://stackoverflow.com/help/mcve), so me and other Firebase developers can help you. – Alex Mamo Jul 28 '19 at 08:29