简体   繁体   中英

Okio source cannot be read inside a Kotlin Coroutine

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 (ie 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:

  1. You are creating a scope that is not associated with the lifecycle of something (activity, fragment, view, whatever). Never create a scope arbitrarily and always launch into one which is tied to some other lifecycle (ie, has a call to cancel() somewhere).
  2. You are opening the file on the calling thread (main thread). Okio.source(File) opens the file which involves file I/O that can block. Open the file inside the coroutine on the IO dispatcher.
  3. You are buffering across a 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.
  4. You are keeping the buffered source alive while waiting for the coroutine to run on the main thread. Allow the 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)
  }  
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM