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

CameraX图片存cacheDir后Coil加载异常及PNG保存崩溃求助

问题解决:CameraX拍照存PNG崩溃&Coil加载不显示

问题根源分析

  1. Coil最初加载无显示:你之前只创建了空的缓存文件,没有把Bitmap数据写入文件,所以Coil加载的是空文件,自然看不到内容。添加compress后写入了JPEG数据,所以能正常加载。
  2. PNG保存崩溃:CameraX返回的Bitmap大概率是HARDWARE配置(GPU显存中的Bitmap),这种Bitmap无法直接调用compress方法进行PNG压缩(JPEG可能因底层处理逻辑未触发崩溃,但PNG会直接抛出异常)。

具体修复方案

1. 转换Bitmap为可压缩的软件格式

在拍照回调里,把CameraX返回的HARDWARE Bitmap转成ARGB_8888格式的软件Bitmap,确保能正常压缩:

controller.takePicture(
    ContextCompat.getMainExecutor(context),
    object : OnImageCapturedCallback() {
        override fun onCaptureSuccess(image: ImageProxy) {
            super.onCaptureSuccess(image)

            val matrix = Matrix().apply {
                postRotate(image.imageInfo.rotationDegrees.toFloat())
            }

            val originalBitmap = image.toBitmap()
            // 把HARDWARE Bitmap转成软件Bitmap
            val softwareBitmap = if (originalBitmap.config == Bitmap.Config.HARDWARE) {
                originalBitmap.copy(Bitmap.Config.ARGB_8888, true)
            } else {
                originalBitmap
            }

            val rotatedBitmap = Bitmap.createBitmap(
                softwareBitmap,
                0,
                0,
                softwareBitmap.width,
                softwareBitmap.height,
                matrix,
                true
            )

            // 回收不用的Bitmap,避免内存泄漏
            originalBitmap.recycle()
            if (softwareBitmap != originalBitmap) {
                softwareBitmap.recycle()
            }

            onPhotoTaken(rotatedBitmap)
        }

        override fun onError(exception: ImageCaptureException) {
            super.onError(exception)
            Log.e("Camera launch", "Couldn't take photo: ", exception)
        }
    }
)

2. 修正ViewModel中的文件写入逻辑

确保先写入Bitmap数据再关闭流,同时PNG的quality参数不影响(传100即可,因为PNG是无损压缩):

try {
    val cacheFile = File(context.cacheDir, "space_${System.currentTimeMillis()}.png")
    Log.i("cachedfile", cacheFile.toString())

    val outPutStream = FileOutputStream(cacheFile)
    _uiState.value.spaceBitmap?.let { bitmap ->
        // PNG压缩,quality参数无效,传100就行
        val isCompressed = bitmap.compress(Bitmap.CompressFormat.PNG, 100, outPutStream)
        if (!isCompressed) {
            Log.w("compress", "PNG压缩失败")
        }
    }
    outPutStream.flush()
    outPutStream.close()

    addSpaceRepository.insertSpace(
        space = Space(
            id = "space_${System.currentTimeMillis()}",
            name = _uiState.value.spaceName.trim(),
            color = _uiState.value.spaceColor.toArgb(),
            iconName = _uiState.value.spaceIcon?.name ?: Icons.Rounded.Kitchen.name,
            fileImageCache = cacheFile.path
        )
    )
} catch (e: Throwable) {
    Log.e("erreur load file", "Cause : ${e.localizedMessage}", e) // 打印完整堆栈,方便排查问题
}

额外优化点

  • IO操作(文件写入、压缩)要放在ViewModel的IO协程里执行,别阻塞主线程:
    viewModelScope.launch(Dispatchers.IO) {
        // 上面的文件写入代码放这里
    }
    
  • 每次使用完Bitmap记得调用recycle(),避免内存溢出。

内容的提问来源于stack exchange,提问作者Mehdi.ncb

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 04:57:12