I am trying to generate a Bitmap from an Okio source using this implementation
val file = /* ... */
Okio.source(file).use {
CoroutineScope(Dispatchers.IO).launch {
Okio.buffer(source).use { bufferedSource ->
val bitmap = BitmapFactory.decodeStream(bufferedSource.inputStream())
withContext(Dispatchers.Main) {
view.setImageBitmap(bitmap)
}
}
}
}
Thing is, the resulting bitmap is null
and I get the following logs
W/System.err: java.io.IOException: Stream Closed
W/System.err: at java.io.FileInputStream.read(FileInputStream.java:313)
W/System.err: at okio.InputStreamSource.read(Okio.kt:102)
W/System.err: at okio.RealBufferedSource$inputStream$1.read(RealBufferedSource.kt:438)
W/System.err: at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
W/System.err: at android.graphics.BitmapFactory.decodeStreamInternal(BitmapFactory.java:790)
W/System.err: at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:765)
W/System.err: at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:806)
W/System.err: at **************************************$1.invokeSuspend(ImageRenderer.kt:32)
W/System.err: at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
W/System.err: at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
W/System.err: at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
W/System.err: at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:749)
W/System.err: at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
W/System.err: at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
D/skia: ---- read threw an exception
D/skia: --- Failed to create image decoder with message 'unimplemented'
Doing the same operation outside a coroutine (i.e. in the main thread) results in the Bitmap properly decoded. I appreciate your help finding out why the source is not correctly read inside the coroutine.
There's a few things wrong with your snippet:
cancel()
somewhere).Okio.source(File)
opens the file which involves file I/O that can block. Open the file inside the coroutine on the IO dispatcher.use
block. By calling use { launch { } }
you are closing the file before the launched coroutine can run to read the file. Open and buffer the file source together.use
block of the file to complete before transfering the coroutine to the main dispatcher.The final code should look something like this:
val file = /* ... */
scope.launch(Dispatchers.IO) {
val bitmap = Okio.buffer(Okio.source(file)).use { bufferedSource ->
BitmapFactory.decodeStream(bufferedSource.inputStream())
}
withContext(Dispatchers.Main) {
view.setImageBitmap(bitmap)
}
}
Also, consider upgrading your Okio version which is now written in Kotlin and works via extension functions:
val file = /* ... */
scope.launch(Dispatchers.IO) {
val bitmap = file.source().buffer().use { bufferedSource ->
BitmapFactory.decodeStream(bufferedSource.inputStream())
}
withContext(Dispatchers.Main) {
view.setImageBitmap(bitmap)
}
}