进度指示器失效
Describe the bug
根据下载指示器的文档所示使用ktor3作为engine,在加载图片时指示器不显示,会直接从无到有的显示图片,如果继承自AbsProgressPainter并打印drawProgress会发现值一直是0,这个问题从4.0.5版本的sketch就出现了。
Versions
- sketch version*: 4.1.0
- Kotlin version*: 2.2.0-RC2
- Compose version(s)*: 1.9.0-alpha02
- Running Devices (*) (Model, OS, CPU Architecture, JDK.): Windows11 JDK21
Sample code
fun main() {
singleWindowApplication {
val progressPainter = rememberRingProgressPainter()
val state = rememberAsyncImageState()
AsyncImage(
uri = "https://github.githubassets.com/assets/gc_banner_light-24900d64bc7a.png?key=${System.currentTimeMillis()}",
contentDescription = null,
modifier = Modifier.size(400.dp, 200.dp)
.background(Color.Green)
.progressIndicator(state, progressPainter)
)
}
}
Screenshots
你这段代码有一个问题,val state = rememberAsyncImageState() 的 state 并没有传递给 AsyncImage,因此即使有进度也不会更新到你创建的 state ,就更不会再传递到 ProgressIndictor
另外,进度更新逻辑是 300 毫秒更新一次进度,如果 300 毫秒内图片就下载完成了,进度指示器也会立即消失,你不会看到有进度只会看到指示器消失了
你这段代码有一个问题,val state = rememberAsyncImageState() 的 state 并没有传递给 AsyncImage,因此即使有进度也不会更新到你创建的 state ,就更不会再传递到 ProgressIndictor
另外,进度更新逻辑是 300 毫秒更新一次进度,如果 300 毫秒内图片就下载完成了,进度指示器也会立即消失,你不会看到有进度只会看到指示器消失了
哦哦哦,不好意思我忘记打上了,现在是这样的,仍然是有问题不显示指示器,至于图片,我试过大图加载超过5秒的也没有指示器
val progressPainter = rememberRingProgressPainter()
val state = rememberAsyncImageState()
AsyncImage(
uri = "https://github.githubassets.com/assets/gc_banner_light-24900d64bc7a.png?key=${System.currentTimeMillis()}",
state = state,
contentDescription = null,
modifier = Modifier.size(400.dp, 200.dp)
.background(Color.Green)
.progressIndicator(state, progressPainter)
)
超过 5 秒不一定是下载了 5 秒,有可能 http 连接耗时 4.8 秒,真正读数据只有不到 300 毫秒
你可以打印 AbsProgressPainter 的 progress 看看,progress 大于 0 才是第一次读取到数据
singleWindowApplication {
val progressPainter = object : AbsProgressPainter() {
override fun DrawScope.drawProgress(drawProgress: Float) {
println(drawProgress)
}
override val intrinsicSize: Size = Size(100f, 100f)
}
val state = rememberAsyncImageState()
AsyncImage(
uri = "https://github.githubassets.com/assets/gc_banner_light-24900d64bc7a.png?key=${System.currentTimeMillis()}",
state = state,
contentDescription = null,
modifier = Modifier.size(400.dp, 200.dp)
.background(Color.Green)
.progressIndicator(state, progressPainter)
)
}
我试过,drawProgress只会打印一次0.0,没有后续输出了,但是图片确实能加载出。 即使我换了100MB的图片,仍然是没有任何进度指示器。
你把图片给我试试
你把图片给我试试
我用kmp模板生成了一个最小的测试示例,也包含了测试图片,如果需要网络图片,链接是https://img.picgo.net/2025/06/15/1de439443053e5edd.webp
已查明是 KtorStack 的问题,下个版本修复,预计一到两周内发布
已发布 4.2.0-SNAPSHOT 版本,你试一下,SNAPSHOP 版本需要配置快照仓库
已发布 4.2.0-SNAPSHOT 版本,你试一下,SNAPSHOP 版本需要配置快照仓库
请问快照仓库地址是什么?
就是 maven 官方的快照仓库 https://s01.oss.sonatype.org/content/repositories/snapshots/
就是 maven 官方的快照仓库 https://s01.oss.sonatype.org/content/repositories/snapshots/
刚刚试了一下,是4.2.0-SNAPSHOT的,还是不行。drawProgress仍然只会打印一次。 是否可能跟ktor版本有关,我的ktor版本是3.1.3。
刚刚测试了,用sketch的ktor版本3.0.3一样没有指示器。而且是debug就没有,跟proguard应该没关系。
破案了,做个总结吧!
- 之前的版本 KtorStack 确实有问题,它会先将所有内容都读到内存里再开始以回调进度的方式从内存读取,这时非常快就读完了,所以你看不到进度,4.2.0-SNAPSHOT 也已经修复了这个问题了
- 你需要禁用所有缓存才能每次都看到进度,否则只有第一次能看到进度
- 你需要移除 uri 中的时间或者用 remember 包括它,因为请求 uri 中包含了当前的系统时间,每次重组 uri 都会改变导致多次请求共用一个状态,最终导致状态混乱,进度始终更新为-1.0,drawProgress 就只更新一次
- progressPainter 也要用 remember 包括
⚠️切记在 compose ui 中使用 System.currentTimeMillis() 是非常危险的⚠️
正确的代码如下:
fun main() = singleWindowApplication {
val progressPainter = remember {
object : AbsProgressPainter() {
override fun DrawScope.drawProgress(drawProgress: Float) {
println(drawProgress)
}
override val intrinsicSize: Size = Size(100f, 100f)
}
}
val state = rememberAsyncImageState()
val uri = remember { "https://img.picgo.net/2025/06/15/1de439443053e5edd.webp?key=${System.currentTimeMillis()}" }
AsyncImage(
request = ComposableImageRequest(uri) {
memoryCachePolicy(CachePolicy.DISABLED)
resultCachePolicy(CachePolicy.DISABLED)
downloadCachePolicy(CachePolicy.DISABLED)
},
state = state,
contentDescription = null,
modifier = Modifier.size(400.dp, 200.dp)
.background(Color.Green)
.progressIndicator(state, progressPainter)
)
}
破案了,做个总结吧!
- 之前的版本 KtorStack 确实有问题,它会先将所有内容都读到内存里再开始以回调进度的方式从内存读取,这时非常快就读完了,所以你看不到进度,4.2.0-SNAPSHOT 也已经修复了这个问题了
- 你需要禁用所有缓存才能每次都看到进度,否则只有第一次能看到进度
- 你需要移除 uri 中的时间或者用 remember 包括它,因为请求 uri 中包含了当前的系统时间,每次重组 uri 都会改变导致多次请求共用一个状态,最终导致状态混乱,进度始终更新为-1.0,drawProgress 就只更新一次
- progressPainter 也要用 remember 包括
⚠️切记在 compose ui 中使用 System.currentTimeMillis() 是非常危险的⚠️
哦哦哦对对对,我本来是通过当前时间造uri来实现每次重启程序去掉缓存测试的,之前带上后面我忘记remember了,现在是没有问题的了,非常感谢!
4.2.0-SNAPSHOT指示器没问题了,但是和1.3.0版本的zoomimage使用会崩溃 (NoClassDefFoundError: com/github/panpf/sketch/util/Compose_core_utilsKt) 位于com.github.panpf.zoomimage.compose.sketch.internal.AnimatableSketchComposeSubsamplingImageGenerator.generateImage(line: 41) 应该是4.2.0弃用了一些东西导致的吧。
我就先等您之后发布4.2.0的release后和zoomimage同步一下版本再用指示器吧,非常感谢!😊
sketch 和 zoomimage 都已发布新的 beta 版