You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Coil Uri将Bitmap加载到Glance Composable并读取磁盘缓存

问题描述

我参考Glance的ImageGlanceWidget示例代码,尝试通过Uri将Bitmap加载到Glance中。按照相关回答的注释替换getImageProvider()方法后,运行时出现错误:
Error in Glance App Widget java.io.FileNotFoundException: No content provider:/data/user/0/com.example.myappname/cache/image_cache/56448.....8e0ad2fac0.1

请问如何从Coil磁盘缓存中获取Bitmap?

当前代码

getImageProvider方法

private fun getImageProvider(context: Context, coilPath: String): ImageProvider {
    val bitmap = MediaStore.Images.Media.getBitmap(context.contentResolver, coilPath.toUri())
    return ImageProvider(bitmap)
}

参数值

val coilPath = context.imageLoader.diskCache?.get("https://picsum.photos/${width.roundToInt()}/${height.roundToInt()}")?.data?.toFile()?.path!! /*使用Worker上下文*/
val context = LocalContext.current /*从Glance Composable传入上下文到getImageProvider*/

完整堆栈跟踪

E/GlanceAppWidget: Error in Glance App Widget
    java.io.FileNotFoundException: No content provider: /data/user/0/com.example.mpappname/cache/image_cache/56448c7214ea904b18f01d34bdd997afd53c1c976abda4a67546d08e0ad2fac0.1
        at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:2013)
        at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1842)
        at android.content.ContentResolver.openInputStream(ContentResolver.java:1518)
        at android.provider.MediaStore$Images$Media.getBitmap(MediaStore.java:2322)
        at com.example.stockwidgetv1.clean.widget.glance.utility.WidgetDummy.getImageProvider2(WidgetDummy.kt:145)
        at com.example.stockwidgetv1.clean.widget.glance.utility.WidgetDummy.access$getImageProvider2(WidgetDummy.kt:44)
        at com.example.stockwidgetv1.clean.widget.glance.utility.WidgetDummy$Content$1.invoke(WidgetDummy.kt:101)
        at com.example.stockwidgetv1.clean.widget.glance.utility.WidgetDummy$Content$1.invoke(WidgetDummy.kt:97)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
        at androidx.glance.layout.BoxKt.Box(Box.kt:74)
        at com.example.stockwidgetv1.clean.widget.glance.utility.WidgetDummy.Content(WidgetDummy.kt:92)
        at androidx.glance.appwidget.GlanceAppWidget$setContent$1$1.invoke(GlanceAppWidget.kt:404)
        at androidx.glance.appwidget.GlanceAppWidget$setContent$1$1.invoke(GlanceAppWidget.kt:404)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
        at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
        at androidx.glance.appwidget.GlanceAppWidget$setContent$1.invoke(GlanceAppWidget.kt:398)
        at androidx.glance.appwidget.GlanceAppWidget$setContent$1.invoke(GlanceAppWidget.kt:397)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
        at androidx.compose.runtime.ActualJvm_jvmKt.invokeComposable(ActualJvm.jvm.kt:78)
        at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:3248)
        at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:3238)
        at androidx.compose.runtime.SnapshotStateKt__DerivedStateKt.observeDerivedStateRecalculations(DerivedState.kt:341)
        at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(Unknown Source:1)
        at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3238)
        at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(Composer.kt:3173)
        at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:587)
        at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:950)
        at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:519)
        at androidx.glance.appwidget.GlanceAppWidget.setContent-Cox8Y-g(GlanceAppWidget.kt:397)
        at androidx.glance.appwidget.GlanceAppWidget.access$setContent-Cox8Y-g(GlanceAppWidget.kt:72)
        at androidx.glance.appwidget.GlanceAppWidget$composeForSize$2.invokeSuspend(GlanceAppWidget.kt:372)
        at androidx.glance.appwidget.GlanceAppWidget$composeForSize$2.invoke(Unknown Source:8)
        at androidx.glance.appwidget.GlanceAppWidget$composeForSize$2.invoke(Unknown Source:4)
        at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:89)
        at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:169)
        at kotlinx.coroutines.BuildersKt.withContext(Unknown Source:1)
        at androidx.glance.appwidget.GlanceAppWidget.composeForSize-AAqiGWc$glance_appwidget_release(GlanceAppWidget.kt:365)
        at androidx.glance.appwidget.GlanceAppWidget$Api31Impl$composeAllSizes$2$allViews$1$1.invokeSuspend(GlanceAppWidget.kt:431)
E/GlanceAppWidget:     at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
解决方案

报错原因是直接用文件路径转Uri后,MediaStore.Images.Media.getBitmap无法访问应用私有缓存目录的文件——该方法需要内容提供者支持,但私有目录文件没有对应的内容提供者。

可以通过以下两种方式解决:

方式一:直接从缓存文件读取Bitmap

跳过MediaStore,直接用BitmapFactory加载本地文件:

private fun getImageProvider(context: Context, coilPath: String): ImageProvider {
    val file = File(coilPath)
    val bitmap = BitmapFactory.decodeFile(file.absolutePath)
    return ImageProvider(bitmap)
}

方式二:用Coil API直接获取Bitmap(推荐)

无需手动处理缓存路径,直接调用Coil的API获取缓存的Bitmap,逻辑更简洁且不易出错:

// 需在协程环境中调用(如Worker或Glance的suspend方法)
private suspend fun getImageProvider(context: Context, url: String, width: Int, height: Int): ImageProvider {
    val request = ImageRequest.Builder(context)
        .data(url)
        .size(width.roundToInt(), height.roundToInt())
        .build()
    val result = context.imageLoader.execute(request).await()
    val bitmap = (result.drawable as BitmapDrawable).bitmap
    return ImageProvider(bitmap)
}

注意事项

  1. 在Glance Composable中调用时,要确保在suspend函数内执行,因为Coil的await()是挂起函数。
  2. 应用私有缓存目录的文件仅能被应用自身访问,直接用BitmapFactory.decodeFile可以正常读取,无需依赖内容提供者。

内容的提问来源于stack exchange,提问作者mars8

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 22:35:18