Android相机拍摄照片无法加载问题求助(仅三星S7出现)
这个问题在三星Galaxy S7(Android 8)上出现,核心原因大概率是三星相机应用的异步文件写入机制:当你拍完照后,系统并没有立刻将完整的图片数据写入你指定的文件,而是在后台异步处理。这时候你马上调用裁剪库去解码文件,文件要么是空的,要么数据不完整,导致BitmapFactory.decodeStream返回null;而调试时等待的这段时间,刚好让系统完成了文件写入,所以偶尔能成功加载。
结合你的代码和设备特性,给出以下针对性解决方案:
1. 确保文件写入完成后再加载
最直接的解决方式是等待系统完成图片文件的写入,推荐使用MediaScannerConnection来监听文件就绪状态,它会在系统扫描完文件后回调通知,确保文件可以被正常读取:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_TAKE_PHOTO && resultCode == Activity.RESULT_OK) { // 将FileProvider Uri转为真实文件路径 String filePath = getRealPathFromUri(photoUri); if (filePath != null) { MediaScannerConnection.scanFile(this, new String[]{filePath}, new String[]{"image/jpeg"}, new MediaScannerConnection.OnScanCompletedListener() { @Override public void onScanCompleted(String path, Uri uri) { // 扫描完成后再加载图片 updateImageUI(photoUri); } }); } else { showGenericErrorDialog(); } } } // 辅助方法:从FileProvider的Uri获取真实文件路径 private String getRealPathFromUri(Uri uri) { String[] projection = {MediaStore.Images.Media.DATA}; Cursor cursor = getContentResolver().query(uri, projection, null, null, null); if (cursor == null) return null; int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); String path = cursor.getString(columnIndex); cursor.close(); return path; }
2. 修复InputStream未关闭的资源泄漏问题
你的decodeSampledBitmapFromUri方法中打开了InputStream但没有关闭,这在部分设备(比如三星)上可能导致文件资源被占用,影响后续读取。改用try-with-resources语法自动关闭流:
public static Bitmap decodeSampledBitmapFromUri(Context context, Uri sourceUri, int requestSize) { // 使用try-with-resources自动关闭InputStream try (InputStream is = context.getContentResolver().openInputStream(sourceUri)) { if (is == null) { Logger.e("InputStream is null for uri: " + sourceUri); return null; } BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = Utils.calculateInSampleSize(context, sourceUri, requestSize); options.inJustDecodeBounds = false; return BitmapFactory.decodeStream(is, null, options); } catch (IOException e) { Logger.e("Error opening input stream: " + e.getMessage(), e); return null; } }
3. 检查FileProvider路径配置的正确性
观察你的URI示例content://my.package.provider/%2Fmnt%2Fsdcard%2F/ocr/picture9052876345621346933.jpg,路径里出现了重复的斜杠(%2Fmnt%2Fsdcard%2F/ocr/),这可能是FileProvider的路径配置和你的DATA_PATH冲突导致的。
检查res/xml/file_paths.xml,确保配置正确指向你的ocr目录:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 配置外部存储的ocr目录 --> <external-path name="ocr_images" path="ocr/" /> </paths>
同时建议将DATA_PATH改为动态获取,避免硬编码路径:
private final String DATA_PATH = Environment.getExternalStorageDirectory() + "/ocr/";
4. 备用方案:通过MediaStore获取最新拍摄的图片
如果上述方法仍有问题,可以绕过直接读取自己创建的文件,改为从MediaStore查询最新拍摄的图片,确保拿到的是系统已经处理完成的文件:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_TAKE_PHOTO && resultCode == Activity.RESULT_OK) { // 查询MediaStore中最新添加的图片 String[] projection = {MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA}; String sortOrder = MediaStore.Images.Media.DATE_ADDED + " DESC"; Cursor cursor = getContentResolver().query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null, null, sortOrder ); if (cursor != null && cursor.moveToFirst()) { String imagePath = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); photoUri = Uri.fromFile(new File(imagePath)); updateImageUI(photoUri); cursor.close(); } else { // fallback到原方式 updateImageUI(photoUri); } } }
注意:这种方式需要申请READ_EXTERNAL_STORAGE权限(Android 6及以上需要动态申请)。
内容的提问来源于stack exchange,提问作者jack_the_beast

