So the return function of my view model gives no return where in it is expected to give a return of bitmap so it can be used in UI to set the image.
Code of View Model :
val bitmap : MutableLiveData<Bitmap> by lazy { MutableLiveData<Bitmap>() }
fun retrive(doc_name : String,uid: String){
viewModelScope.launch(Dispatchers.IO){
bitmap.postValue(repository.retrive(doc_name,uid))
}
}
Code of Repository:
var localfile = createTempFile("tempImage", null)
var bitmap :Bitmap? = null
override suspend fun retrive(doc_name:String,uid: String) : Bitmap?{
val storageRef = FirebaseStorage.getInstance().reference?.child("/image/8WEFQnomCEMtlaSkCIkrBgT7XeO2/download")
storageRef.getFile(localfile).addOnSuccessListener {
bitmap = BitmapFactory.decodeFile(localfile.absolutePath)
}
return bitmap
}
Code in Fragment inside on View Created part:
val obsover = Observer<Bitmap>{
image.setImageBitmap(it)
}
admin_viewmodel.bitmap.observe(viewLifecycleOwner,obsover)
So because I kept in my Repository function that bitmap can be null it opens the fragment with no image in image view But if I keep the Bitmap to not be null(!!) the app crashes and gives the Null Pointer Exception error in the lines below:
Inside the repository code I shared above:
return bitmap!!
2.Inside the View Model code I shared above:
bitmap.postValue(repository.retrive(doc_name,uid))
What I think is its Unable to return because things are working on different threads. Kindy help me solve this, Thanks.
Edit after Broot Reply code changes:
override suspend fun retrive(doc_name:String,uid: String) : Bitmap {
return suspendCoroutine { cont ->
val storageRef =
FirebaseStorage.getInstance().reference?.child("/image/0XhL4jD4XCemk38rcRkIEjJMgjh2/Aadhar")
val localfile = createTempFile("tempImage", null)
storageRef.getFile(localfile).addOnSuccessListener {
val x = cont.resume(bitmap!!)
Log.d("checck", "$x")
}
}
}
CodePudding user response:
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 {
throw IOException(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)
}