如何通过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) }
注意事项
- 在Glance Composable中调用时,要确保在
suspend函数内执行,因为Coil的await()是挂起函数。 - 应用私有缓存目录的文件仅能被应用自身访问,直接用
BitmapFactory.decodeFile可以正常读取,无需依赖内容提供者。
内容的提问来源于stack exchange,提问作者mars8
相关产品推荐
相关产品推荐

