Android 12 三星A32设备Camera2保存图片损坏问题求助
问题根因
该故障是三星特定系统版本的硬件抽象层、系统媒体解码器容错性低,结合代码实现不严谨共同触发的,核心原因有两点:
- JPEG数据读取逻辑存在兼容缺陷:现有代码直接读取
Image对象planes[0]缓冲区的全部剩余字节写入文件,该逻辑在绝大多数设备上可正常运行,但SM-A326B在2022年6月安全补丁版本的Camera2 HAL存在实现差异:返回的JPEG格式Image的plane0缓冲区,在真实JPEG数据结束标记(0xFFD9)后附带了长度不等的内存填充垃圾数据。该设备原生系统图库、媒体解码器对JPEG格式校验严格,遇到结束标记后存在冗余数据的文件会直接判定为损坏,解码输出全白;第三方图片解码器通常会自动扫描定位JPEG结束标记截断数据,因此可以正常恢复显示。 - EXIF写入逻辑不符合标准规范:现有
saveExifData方法中,手动调用setAttribute给TAG_GPS_LATITUDE、TAG_GPS_LONGITUDE传入十进制度数字符串,不符合EXIF标准要求的「度:分:秒」有理数字符串格式。虽然后续调用了setLatLong()方法会尝试写入正确格式的经纬度数据,但该版本三星系统的ExifInterface实现不会覆盖同TAG的已有错误值,会把格式错误的GPS字段写入EXIF块,进一步提升系统解码器判定文件损坏的概率。
修复方案
可直接按以下步骤修改代码,全机型兼容:
- 修复JPEG数据读取逻辑,自动截断有效数据段
替换原有直接读取全量缓冲区的逻辑,写入前先定位JPEG结束标记,只写入有效数据,修改后的saveCameraImage核心逻辑如下:
public void saveCameraImage(Image mImage, File mFile, String mOrientation, Context context) { ByteBuffer buffer = mImage.getPlanes()[0].getBuffer(); byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes); // 定位JPEG标准结束标记0xFFD9,截断HAL返回的冗余填充数据 int jpegEndPos = bytes.length - 2; for (; jpegEndPos >= 0; jpegEndPos--) { if ((bytes[jpegEndPos] & 0xFF) == 0xD9 && (bytes[jpegEndPos - 1] & 0xFF) == 0xFF) { break; } } int validDataLength = jpegEndPos + 1; FileOutputStream fileOutputStream = null; try { fileOutputStream = new FileOutputStream(mFile); fileOutputStream.write(bytes, 0, validDataLength); // 主动刷新IO缓存,避免极端场景下写入不完整 fileOutputStream.flush(); } catch (IOException e) { wil.WriteFile("saveImage - Exception: " + e.getMessage(), context); } finally { mImage.close(); if (null != fileOutputStream) { try { fileOutputStream.close(); } catch (IOException e) { wil.WriteFile("saveImage - Exception: " + e.getMessage(), context); } finally { saveExifData(mFile.getPath(), context); createThumbs(mOrientation, mFile, context); } } } }
- 移除错误的EXIF GPS字段手动赋值
删除saveExifData方法中以下两行错误代码,setLatLong()是系统提供的标准API,会自动写入符合格式要求的经纬度字段,无需手动赋值:
// 删掉这两行 // exifInterface.setAttribute(ExifInterface.TAG_GPS_LATITUDE, String.valueOf(position.getLat())); // exifInterface.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, String.valueOf(position.getLng()));
其他注意事项
- 清单文件中
<uses-feature android:name="android.hardware.location.gps " />配置项末尾存在多余空格,可能导致应用市场硬件特征匹配异常,建议删除该空格。 - 建议将
mImage.close()操作调整到字节数组读取、文件流写入完成之后执行,避免部分低版本OEM设备上缓冲区提前释放导致读取到脏数据。
验证方式
- 修改后在故障设备上拍摄图片,校验生成的文件是否严格以
0xFFD9结尾,系统图库可正常打开无全白、损坏提示 - 回归验证其他机型的拍摄、保存、EXIF写入功能正常即可
内容的提问来源于stack exchange,提问作者Mattia
相关产品推荐
相关产品推荐

