Why Does My Function That Calls an API or Launches a Coroutine Return an Empty or Null Value

Can't get Firestore to return a field value from a collection in Android Studio

Any code that needs to have access to the data from the database, has to be inside the completion listener that gets called when that data is available.

So:

docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
@Override
public void onComplete(@NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
String dbValue = document.getString("password");
tvMenu.setText(dbValue); // br> } else {
Log.d("TAG", "No such document");
}
} else {
Log.d("TAG", "get failed with ", task.getException());
}
}
});

For more on this, see:

  • How to check a certain data already exists in firestore or not
  • Why does my function that calls an API or launches a coroutine return an empty or null value?
  • How to return a DocumentSnapShot as a result of a method?
  • getContactsFromFirebase() method return an empty list
  • Setting Singleton property value in Firebase Listener

My function iss returning a value before the code in the inner class finishes

From the docs:

onDataChange: This method will be called with a snapshot of the data at this location. It will also be called each time that data changes.

When the data changes, whatever is inside the method will be executed. In other words, the method onDataChange instructs the program what to do when it gets results (which could be whenever)

This is asynchronous behaviour - as @Turing85 points out, this will only be called when the data changes.

If you want to know if something with this key exists, you could construct a query and then call get() method which would return a Task<DataSnapshot>. For this task, you could attach addOnCompleteListener listener to do what you would like. This would also be asynchronous, so it would only run when there is a result.

Return is not returning the value

Your original code was incorrect because it fires off an asynchronous function to the API and then returns immediately, before that asynchronous work is done and has fired its callback to update the bitmap property.

Your second code is wrong, because it tries to resume the continuation with the value of the property bitmap, which you have not updated with the value that was returned in the callback. Also, since you're just wanting a Bitmap from the cloud file, there's no reason to download it to a temporary file. You can work directly with the bytes. And there's no reason to use a property that I can see. bitmap can be a local variable.

Also, since you don't do anything in case of failure, your function would hang if there is a problem retrieving the data from Firebase. Below, I just throw the error, but you could do something different like returning null if you want.

I don't know what you're doing with those two parameters, but I left them. I leave it up to you to decide what your byte limit should be (I just used 5 million bytes). I don't remember the guaranteed minimum amount of available memory is for an Android app, and you might know that the file you're retrieving is below that value anyway.

override suspend fun retrive(doc_name: String, uid: String): Bitmap = suspendCoroutine { cont ->
val storageRef =
FirebaseStorage.getInstance().reference.child("/image/0XhL4jD4XCemk38rcRkIEjJMgjh2/Aadhar")
storageRef.getBytes(5_000_000L).addOnSuccessListener { byteArray ->
val bitmap = BitmapFactory.decodeByteArray(byteArray)
cont.resume(bitmap)
}.addOnFailureListener {
cont.resumeWithException(it)
}
}

However: Firebase already comes with the await() extension suspend function so you don't have to use suspendCoroutine.

override suspend fun retrive(doc_name: String, uid: String): Bitmap {
val storageRef =
FirebaseStorage.getInstance().reference.child("/image/0XhL4jD4XCemk38rcRkIEjJMgjh2/Aadhar")
val byteArray = storageRef.getBytes(5_000_000L).await()
return BitmapFactory.decodeByteArray(byteArray)
}

Since decoding a bitmap is kind of a heavy operation, I would do this in Dispatchers.Default:

override suspend fun retrive(doc_name: String, uid: String): Bitmap = withContext(Dispatchers.Default) {
val storageRef =
FirebaseStorage.getInstance().reference.child("/image/0XhL4jD4XCemk38rcRkIEjJMgjh2/Aadhar")
val byteArray = storageRef.getBytes(5_000_000L).await()
return BitmapFactory.decodeByteArray(byteArray)
}


Related Topics



Leave a reply



Submit