FireStore OnFailureListener is not called - java

I am searching some objects with their names in the FIRESTORE database and i have attached an Onsuccess and onfailure Listener to my get method but if some objects are not found it does not execute the body of onfailure
i have tried debuging it but failed to understand the issue.
i have also searched through many articles but it seems no one has encountered this type of error before.I have attached the screenshot from my logcat
below is my code
#Override
public void onResponse(Call<List<DeviceEntity>> call, final Response<List<DeviceEntity>> response) {
if (response.body() != null) {
Log.i("Test", "onResponse: got the device name searching it in firestore");
for (int i = 0; i < response.body().size(); i++) {
final int finalI = i;
Log.i("Test", "Devices that matched the saerch"+ response.body().get(finalI).getDeviceName());
FirebaseFirestore firebaseFirestore=FirebaseFirestore.getInstance();
firebaseFirestore.collection("Mobiles").document(response.body().get(finalI).getDeviceName())
.get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
MobileDataBaseObjectClass mb=documentSnapshot.toObject(MobileDataBaseObjectClass.class);
if (mb != null) {
mb.setTimeStamp(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()));
Log.i(TAGo, "Success:getting the info from firestore");
new insertAsyncTask(mdao,"firestore").execute(mb);
}
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.i(TAGo,"Onfailure listner"+e.toString());
}
});
enter image description here

the comment by Frank van Puffelen is the right answer.
onFailure only executes when there was a problem reading the document (e.g. you don't have access to it). If the document doesn't exist, it should trigger onSuccess. – Frank van Puffelen

Related

How to change document in Firestore without String error?

In my app a first time user creates a username which I save in Firestore as a document and set it as displayName (username = name of document = displayName). In the app the user has the option to change his username by typing a new one in an EditText. The result should be that the data which is stored under his current username moves to a new document which has the new username. So first I check if the new username is already in use by someone else, if not I create a new document which name is the new username. Now I get the current displayName (which is the current username) as a string and the input of the EditText as a string (new username). I Implemented a method to move documents from here: How to move a document in Cloud Firestore?
But when I call the method like this moveFirestoreDocument(usernameInput, oldUsername ); I get:
moveFirestoreDocument cannot be applied to (String, String)
How can I resolve this problem? Can I change the moveFirestoreDocument method so it can also takes Strings?
Here is the method:
public void moveFirestoreDocument(DocumentReference fromPath, final DocumentReference toPath) {
fromPath.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document != null) {
toPath.set(document.getData())
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "DocumentSnapshot successfully written!");
fromPath.delete()
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "DocumentSnapshot successfully deleted!");
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(TAG, "Error deleting document", e);
}
});
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(TAG, "Error writing document", e);
}
});
} else {
Log.d(TAG, "No such document");
}
} else {
Log.d(TAG, "get failed with ", task.getException());
}
}
});
}
You get the following error:
I get "moveFirestoreDocument cannot be applied to (String, String)
Because you are trying to pass to the moveFirestoreDocument() method two arguments of type String and not of type DocumentReference. In order to be able to move a document, both fromPath and toPath must be of type DocumentReference so you can call set() on toPath and get() and delete() on fromPath.
Can I change the moveFirestoreDocument method so it can also takes Strings?
There is no need to change the method, you can simply pass the correct arguments when you call it. According to the details that you have provided in the question, I understand that you have a schema that is similar to this:
Firestore-root
|
--- users (collection)
|
--- John (document)
|
--- //User details
To rename the document call the method using:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference usersRef = rootRef.collection("users");
DocumentReference oldUsernameRef = usersRef.document(oldUsername);
DocumentReference usernameInputRef = usersRef.document(usernameInput);
moveFirestoreDocument(oldUsernameRef, usernameInputRef);

Every document read in a transaction must also be written : Firestore Transaction Exception in android

Firestore Transactions is giving me the following error :
Caused by: com.google.firebase.firestore.FirebaseFirestoreException: Every document read in a transaction must also be written.
Here's the piece of code:
db.runTransaction(new Transaction.Function<Void>() {
#Override
public Void apply(Transaction transaction) throws FirebaseFirestoreException {
DocumentReference docRef2 = db.collection("ABC").document(mMatchedUserId);
DocumentReference ref3 = db.collection("XYZ").document(mCurrentUserId);
DocumentReference ref4 = db.collection("XYZ").document(mMatchedUserId);
DocumentSnapshot documentSnapshot2 = transaction.get(docRef2);
if(documentSnapshot2.exists())
{
transaction.delete(docRef2);
transaction.set(ref3, myMap1);
transaction.set(ref4,myMap2);
}
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);
}
});
If I remove this if condition :if(documentSnapshot1.exists() && documentSnapshot2.exists()) then the transaction completes successfully and there is no error.
But the major point of setting up this transaction was the if condition. Kindly help.
The problem in your code lies in the fact that inside your transaction you are reading two documents:
DocumentSnapshot documentSnapshot1 = transaction.get(docRef1);
DocumentSnapshot documentSnapshot2 = transaction.get(docRef2);
But you never write them back. As I see in your code, you only need them to a delete operation so in such case, read them before the transaction rather than inside the transaction and your problem will be solved.

Why does the command transaction.update is executed before the carrelloAttuale.prodotti.add() command

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
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:
How to return a DocumentSnapShot as a result of a method?
Firebase Firestore get data from collection
"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.

How to call fusedLocationClient.getLastLocation() from within a JobService?

I'm trying to implement getLastLocation().addOnSuccessListener as documented here: https://developer.android.com/training/location/retrieve-current#last-known but in my case within a JobService which is periodically ran by the JobScheduler.
Here is the meaningful part of my jobService:
public class PeriodicJob extends JobService {
final String TAG = "BNA.Job";
private FusedLocationProviderClient fusedLocationClient;
#Override
public void onCreate() {
super.onCreate();
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
Log.i(TAG, "onCreate");
}
#Override
public boolean onStartJob(JobParameters params) {
Log.d(TAG, "Job started..."); // logs every 15mins
fusedLocationClient.getLastLocation()
.addOnSuccessListener(this, new OnSuccessListener<Location>() {
#Override
public void onSuccess(Location location) {
// Got last known location. In some rare situations this can be null.
if (location != null) {
Log.d(TAG, "got Location: " + location);
Log.d(TAG, "Accuracy:" + location.getAccuracy()+", LON:"
+location.getLongitude() + ", LAT:" + location.getLatitude());
}
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
(...) // never runs
});
return true;
}
When I run this (it compiles fine), I get: Caused by: java.lang.ClassCastException: (...)PeriodicJob cannot be cast to java.util.concurrent.Executor
I looked at Android app crashes on firebase phone authentication and Can't cast this into Executor in google reCAPTCHA but I still couldnt get it working. Just calling .addOnSuccessListener(new OnSuccessListener<Location>() like the second question's answer suggests, throws no error, but the code is never executed (at least, no log is written).
How can I get the call to onSuccess working?
EDIT: In fact it was working all the time (without the first parameter), I had neglected to detect the case when a null location was returned (which I wasnt expecting). Thanks to Mike M.!

Read Data from Cloud FireStore Android

Is this script wrong, because the data I receive is null while I've added data on the Cloud Firestore. I do not use RecyclerView because I only need one data only.
This is the script:
private void getCustomer(){
firestoreDB.collection("customer")
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
customers = new ArrayList<>();
for (DocumentSnapshot doc : task.getResult()) {
Customer customer = doc.toObject(Customer.class);
customer.setId_customer(doc.getId());
customers.add(customer);
}
} else {
// Log.d(TAG, "Error getting documents: ", task.getException());
}
}
});
firestoreListener = firestoreDB.collection("customer")
.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
if (e != null) {
// Log.e(TAG, "Listen failed!", e);
return;
}
customers = new ArrayList<>();
for (DocumentSnapshot doc : documentSnapshots) {
Customer customer = doc.toObject(Customer.class);
customer.setId_customer(doc.getId());
customers.add(customer);
}
}
});
id_customer = customers.get(0).getId_customer();
}
and this is my firestore:
You cannot use something now that hasn't been loaded yet. With other words, you cannot simply use the following line of code:
id_customer = customers.get(0).getId_customer();
Outside the onSuccess() method because it will always be null due the asynchronous behaviour of this method. This means that by the time you are trying to use the id_customer variable outside that method, the data hasn't finished loading yet from the database and that's why is not accessible.
A quick solve for this problem would be to use that result only inside the onSuccess() method, or if you want to use it outside, I recommend you see the last part of my anwser from this post in which I have exaplined how it can be done using a custom callback. You can also take a look at this video for a better understanding.

Categories

Resources