I'm trying to save user information in Firestore. Basically I save the image in Storage, then I save the information like name, email and URL of the image in Firestore. The main problem I'm facing: if inside uploadTask/taskSnapshot I use it only to get the URL of the image and assign it to a String, and outside of uploadTask/taskSnapshot I save all information at once, the String imageURL is null, it only receives the value of uri.tostring inside the uploadTask/taskSnapshot block. Outside, it doesn't matter where I put String imageURL, locally or globally, it always gets null. That way, I can save all the user information, but with a null image. The best way would be is below, that inside uploadTask/taskSnapshot I already saved all the information. At first I had problems doing it this way, because the user was created through FirebaseAuth, the image was uploaded to Storage, but the information was not saved to the firestore. After so many changes it worked perfectly, I thought it was problem solved. But now I'm adding other activities and this one of registering a user stopped working again. Having the same problem of not saving the information in Firestore, it just creates the user through FirebaseAuth and saves the image.
That way it doesn't save the information in Firestore:
private void SalveDataUser() {
loading(true);
userID = FirebaseAuth.getInstance().getCurrentUser().getUid();
DocumentReference documentReference = db.collection(
Constants.KEY_COLLECTION_USERS).document(String.valueOf(userID));
if (imageUri != null) {
String filename = UUID.randomUUID().toString();
final StorageReference reference = FirebaseStorage.getInstance().getReference("/images/" filename);
final UploadTask uploadTask = reference.putFile(imageUri);
uploadTask.addOnSuccessListener(taskSnapshot -> {
taskSnapshot.getStorage().getDownloadUrl().addOnSuccessListener(uri -> {
String imageUrl = uri.toString();
HashMap<String, Object> users = new HashMap<>();
users.put(Constants.KEY_NAME, binding.etName.getText().toString());
users.put(Constants.KEY_IMAGE, imageUrl);
users.put(Constants.KEY_EMAIL, binding.etEmail.getText().toString());
documentReference.set(users)
.addOnSuccessListener(unused -> {
loading(false);
preferenceManager.putBoolean(Constants.KEY_IS_SIGNED_IN, true);
preferenceManager.putString(Constants.KEY_USER_ID, String.valueOf(userID));
preferenceManager.putString(Constants.KEY_IMAGE, imageUrl);
preferenceManager.putString(Constants.KEY_NAME, binding.etName.getText().toString());
Intent intent = new Intent(getApplicationContext(), Profile.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
})
.addOnFailureListener(e -> {
showToast("Erro ao cadastrar usuário: " e);
});
}).addOnFailureListener(e -> {
showToast("Error: " e);
});
}).addOnFailureListener(e -> {
showToast("Error: " e);
});
}
}
This way it saves in Firestore, but the image is null:
private void SalveDataUser() {
loading(true);
userID = FirebaseAuth.getInstance().getCurrentUser().getUid();
DocumentReference documentReference = db.collection(
Constants.KEY_COLLECTION_USERS).document(String.valueOf(userID));
if (imageUri != null) {
String filename = UUID.randomUUID().toString();
final StorageReference reference = FirebaseStorage.getInstance().getReference("/images/" filename);
final UploadTask uploadTask = reference.putFile(imageUri);
final String[] imageUrl = new String[1];
uploadTask.addOnSuccessListener(taskSnapshot -> {
taskSnapshot.getStorage().getDownloadUrl().addOnSuccessListener(uri -> {
imageUrl[0] = uri.toString();
}).addOnFailureListener(e -> {
showToast("Error: " e);
});
}).addOnFailureListener(e -> {
showToast("Error: " e);
});
HashMap<String, Object> users = new HashMap<>();
users.put(Constants.KEY_NAME, binding.etName.getText().toString());
users.put(Constants.KEY_IMAGE, imageUrl[0]);
users.put(Constants.KEY_EMAIL, binding.etEmail.getText().toString());
documentReference.set(users)
.addOnSuccessListener(unused -> {
loading(false);
preferenceManager.putBoolean(Constants.KEY_IS_SIGNED_IN, true);
preferenceManager.putString(Constants.KEY_USER_ID, String.valueOf(userID));
preferenceManager.putString(Constants.KEY_IMAGE, imageUrl[0]);
preferenceManager.putString(Constants.KEY_NAME, binding.etName.getText().toString());
Intent intent = new Intent(getApplicationContext(), Profile.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
})
.addOnFailureListener(e -> {
showToast("Erro ao cadastrar usuário: " e);
});
}
}
IMAGE PRINTSCREEN: This way it saves in Firestore, but the image is null
CodePudding user response:
The problem isn't so much where you access the download URL, but when you access it. Because uploading files, getting their download URL, and writing to Firestore are all asynchronous operations, the order in which they execute is different from what you may expect.
This is easiest to see if you add some logging information:
userID = FirebaseAuth.getInstance().getCurrentUser().getUid();
DocumentReference documentReference = db.collection(
Constants.KEY_COLLECTION_USERS).document(String.valueOf(userID));
if (imageUri != null) {
String filename = UUID.randomUUID().toString();
final StorageReference reference = FirebaseStorage.getInstance().getReference("/images/" filename);
Log.d("Firebase", "Starting image upload");
final UploadTask uploadTask = reference.putFile(imageUri);
uploadTask.addOnSuccessListener(taskSnapshot -> {
taskSnapshot.getStorage().getDownloadUrl().addOnSuccessListener(uri -> {
Log.d("Firebase", "Got download URL");
});
});
Log.d("Firebase", "Writing to Firestore");
HashMap<String, Object> users = new HashMap<>();
users.put(Constants.KEY_NAME, binding.etName.getText().toString());
users.put(Constants.KEY_IMAGE, imageUrl[0]);
users.put(Constants.KEY_EMAIL, binding.etEmail.getText().toString());
documentReference.set(users);
}
If you run this, it logs:
Starting image upload
Writing to Firestore
Got download URL
This is probably not what you expected, but it is by design and does explain why you're seeing null
in the database: by the you write to Firestore, the image upload hasn't completed yet.
The solution for this problem is always the same: any code that requires certain part of data has to be inside the completion listener that runs when that data is available, or it has to be called from there, or otherwise synchronized.
So in your case, the easiest solution is to move the code that writes to Firestore into the completion handler for the download URL:
uploadTask.addOnSuccessListener(taskSnapshot -> {
taskSnapshot.getStorage().getDownloadUrl().addOnSuccessListener(uri -> {
String imageUrl = uri.toString();
HashMap<String, Object> users = new HashMap<>();
users.put(Constants.KEY_NAME, binding.etName.getText().toString());
users.put(Constants.KEY_IMAGE, imageUrl[0]);
users.put(Constants.KEY_EMAIL, binding.etEmail.getText().toString());
documentReference.set(users)
.addOnSuccessListener(unused -> {
loading(false);
preferenceManager.putBoolean(Constants.KEY_IS_SIGNED_IN, true);
preferenceManager.putString(Constants.KEY_USER_ID, String.valueOf(userID));
preferenceManager.putString(Constants.KEY_IMAGE, imageUrl);
preferenceManager.putString(Constants.KEY_NAME, binding.etName.getText().toString());
Intent intent = new Intent(getApplicationContext(), Profile.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
})
.addOnFailureListener(e -> {
showToast("Erro ao cadastrar usuário: " e);
});
}).addOnFailureListener(e -> {
showToast("Error: " e);
});
}).addOnFailureListener(e -> {
showToast("Error: " e);
});
Also see:
- getContactsFromFirebase() method return an empty list
- Setting Singleton property value in Firebase Listener
CodePudding user response:
After so many modifications, I managed to make it work perfectly. I used continueWithTask() before adding, after that I used addOnCompleteListener() to write to Firestore
Solução:
private void SalveDataUser(View v) {
loading(true);
userID = Objects.requireNonNull(FirebaseAuth.getInstance().getCurrentUser()).getUid();
if (imageUri != null) {
String filename = UUID.randomUUID().toString();
final StorageReference reference = FirebaseStorage.getInstance().getReference("/images/" filename);
UploadTask uploadTask = reference.putFile(imageUri);
Task<Uri> uriTask = uploadTask.continueWithTask(task -> {
if (!task.isSuccessful()) {
throw Objects.requireNonNull(task.getException());
}
return reference.getDownloadUrl();
}).addOnCompleteListener(task -> {
if (task.isSuccessful()) {
Uri downloadUri = task.getResult();
Map<String, Object> users = new HashMap<>();
users.put(Constants.KEY_NAME, binding.etName.getText().toString());
users.put(Constants.KEY_IMAGE, downloadUri.toString());
users.put(Constants.KEY_EMAIL, binding.etEmail.getText().toString());
DocumentReference documentReference = db.collection(
Constants.KEY_COLLECTION_USERS).document(userID);
documentReference.set(users)
.addOnSuccessListener(unused -> {
loading(false);
preferenceManager.putBoolean(Constants.KEY_IS_SIGNED_IN, true);
preferenceManager.putString(Constants.KEY_USER_ID, userID);
preferenceManager.putString(Constants.KEY_IMAGE, downloadUri.toString());
preferenceManager.putString(Constants.KEY_NAME, binding.etName.getText().toString());
Intent intent = new Intent(getApplicationContext(), Profile.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
showToast("Cadastro realizado com sucesso");
})
.addOnFailureListener(e -> {
showToast("Erro ao cadastrar " e);
});
}
});
}
}