CameraX图片存cacheDir后Coil加载异常及PNG保存崩溃求助
问题解决:CameraX拍照存PNG崩溃&Coil加载不显示
问题根源分析
- Coil最初加载无显示:你之前只创建了空的缓存文件,没有把Bitmap数据写入文件,所以Coil加载的是空文件,自然看不到内容。添加
compress后写入了JPEG数据,所以能正常加载。 - 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
相关产品推荐
相关产品推荐

