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

Coil SubcomposeAsyncImage的onSuccess/onError回调未触发问题求助

问题:SubcomposeAsyncImage循环调用时onError未触发两次的问题

我编写了一个使用SubcomposeAsyncImage从服务器下载图片的Composable函数(已简化以方便理解):

@Composable
fun BannerReference(
    onImageLoadingState: (State) -> Unit = {}
) {
        SubcomposeAsyncImage(
            model = image,
            onLoading = { onImageLoadingState.invoke(it) },
            onSuccess = { onImageLoadingState.invoke(it) },
            onError = { onImageLoadingState.invoke(it) },
            contentDescription = null,
        )
}

该Composable函数在forEach循环中被多次调用:

referralProgramsData.mgmParams.products.forEachIndexed { index, product ->

            BannerReference(
                iconUrl = product.bannerImage,
                iconUrlFromRes = getBannerImagePlaceholder(product.type),
                onImageLoadingState = { state ->
                    Log.d("debuggg", "screen state: ${state}")
                    imageLoadingState.invoke(ImageLoadingStatuses(index, state))
                }
            )
        }

当BannerReference被调用两次时,有时会得到如下日志:

2023-08-01 12:03:14.951  8492-8492  debuggg                 D  onLoading
2023-08-01 12:03:14.951  8492-8492  debuggg                 D  screen state: Loading(painter=null)
2023-08-01 12:03:14.967  8492-8492  debuggg                 D  onLoading
2023-08-01 12:03:14.967  8492-8492  debuggg                 D  screen state: Loading(painter=null)
2023-08-01 12:03:15.078  8492-8492  debuggg                 D  onError
2023-08-01 12:03:15.078  8492-8492  debuggg                 D  screen state: Error(painter=null, result=coil.request.ErrorResult@f10cd10e)

我期望onError被调用两次,但实际并未触发。请问如何处理这种onImageLoadingState同时调用但有时未执行的情况?


解决方案

1. 给循环中的Composable添加唯一Key

Compose的重组机制会复用无唯一标识的组件实例,导致部分回调丢失。必须给每个BannerReference绑定唯一Key:

referralProgramsData.mgmParams.products.forEachIndexed { index, product ->
    BannerReference(
        key = product.id, // 优先用产品唯一ID,也可临时用index
        iconUrl = product.bannerImage,
        iconUrlFromRes = getBannerImagePlaceholder(product.type),
        onImageLoadingState = { state ->
            Log.d("debuggg", "[$index] screen state: ${state}") // 加index区分日志
            imageLoadingState.invoke(ImageLoadingStatuses(index, state))
        }
    )
}

2. 修正BannerReference的参数绑定

简化后的代码中model = image未关联传入的iconUrl,导致部分组件未发起正确请求,修正后确保每个组件对应独立的图片地址:

@Composable
fun BannerReference(
    iconUrl: String,
    iconUrlFromRes: Int,
    onImageLoadingState: (State) -> Unit = {}
) {
    SubcomposeAsyncImage(
        model = iconUrl,
        placeholder = { painterResource(id = iconUrlFromRes) },
        onLoading = { onImageLoadingState(it) },
        onSuccess = { onImageLoadingState(it) },
        onError = { onImageLoadingState(it) },
        contentDescription = null,
    )
}

3. 避免缓存导致的回调丢失

Coil的缓存机制可能复用之前的错误状态,给每个请求添加唯一缓存键,强制独立处理:

@Composable
fun BannerReference(
    iconUrl: String,
    iconUrlFromRes: Int,
    index: Int,
    onImageLoadingState: (State) -> Unit = {}
) {
    val request = ImageRequest.Builder(LocalContext.current)
        .data(iconUrl)
        .memoryCacheKey("$iconUrl-$index")
        .diskCacheKey("$iconUrl-$index")
        .build()

    SubcomposeAsyncImage(
        model = request,
        placeholder = { painterResource(id = iconUrlFromRes) },
        onLoading = { onImageLoadingState(it) },
        onSuccess = { onImageLoadingState(it) },
        onError = { onImageLoadingState(it) },
        contentDescription = null,
    )
}

4. 验证日志输出完整性

在日志中加入索引标识,确认是否是日志合并导致的视觉上的“未触发”,而非实际回调丢失。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 11:23:17