Okio源无法在Kotlin Coroutine中读取

发布于 2025-02-13 22:58:46 字数 2047 浏览 1 评论 0原文

我正在尝试使用此实现来从OKIO源生成一个位图

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)
            }
        }
    }   
}

,结果是null,并且我

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'

在Coroutine(即主线程中)在Coroutine(即Main线程中)进行以下日志,结果在位图中正确解码。感谢您的帮助,了解为什么源为何在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 (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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

柏拉图鍀咏恒 2025-02-20 22:58:46

片段有一些问题:

  1. 您正在创建与某物生命周期无关的范围(活动,碎片,视图等)。切勿任意创建一个范围,并始终启动到与其他生命周期相关的范围(即,在某处有一个呼叫cancel())。
  2. 您正在调用线程(主线程)上打开文件。 okio.source(file)打开涉及可以阻止的文件I/O的文件。打开IO调度器上Coroutine内部的文件。
  3. 您正在使用块中进行缓冲。通过调用使用{启动{}}您要关闭文件,然后在启动的coroutine可以运行以读取文件之前。一起打开并缓冲文件源。
  4. 在等待Coroutine在主线程上运行时,您将保持缓冲源的活力。允许使用文件块在将Coroutine转移到主调度器之前完成。

最终代码应该看起来像这样:

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)
  }  
}

另外,考虑升级您的OKIO版本,该版本现在用Kotlin编写,并通过扩展功能工作:

val file = /* ... */
    
scope.launch(Dispatchers.IO) {
  val bitmap = file.source().buffer().use { bufferedSource ->
    BitmapFactory.decodeStream(bufferedSource.inputStream())
  }

  withContext(Dispatchers.Main) {
    view.setImageBitmap(bitmap)
  }  
}

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 (i.e., 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)
  }  
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文