Google Barcode Scanning ML Kit在Android 8.1及以下版本图库扫码失效求助
Android 8及以下版本图库图片扫码失败解决方案
在Android 8(API Level <28)及以下版本中,使用InputImage.fromFilePath处理图库返回的content://类型Uri时,会因系统文件访问限制、图片Exif方向处理逻辑差异等问题,导致ML Kit无法正确解析图片内容,进而无法识别二维码。相机扫码不受影响是因为相机流的图片处理路径与图库图片完全不同。
解决方案:通过Bitmap创建InputImage并处理图片方向
将图库Uri转换为Bitmap时同步处理图片的Exif方向,再用调整后的Bitmap创建InputImage,即可解决低版本的识别问题。
1. 添加工具方法
// 将Uri转换为Bitmap并处理Exif方向 private Bitmap getBitmapFromUri(Uri uri) throws IOException { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = false; // 获取原始图片Bitmap Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri); // 读取图片Exif方向信息 ExifInterface exif = new ExifInterface(getRealPathFromUri(uri)); int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); // 根据Exif信息旋转Bitmap Matrix matrix = new Matrix(); switch (orientation) { case ExifInterface.ORIENTATION_ROTATE_90: matrix.postRotate(90); break; case ExifInterface.ORIENTATION_ROTATE_180: matrix.postRotate(180); break; case ExifInterface.ORIENTATION_ROTATE_270: matrix.postRotate(270); break; default: break; } return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); } // 将content 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 uri.getPath(); int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); String path = cursor.getString(columnIndex); cursor.close(); return path; }
2. 修改图库选择回调代码
mChoosePhoto = registerForActivityResult(new ActivityResultContracts.GetContent(), result -> { if (result == null) return; BarcodeScannerOptions options = new BarcodeScannerOptions.Builder() .setBarcodeFormats(Barcode.FORMAT_QR_CODE) .build(); BarcodeScanner scanner = BarcodeScanning.getClient(options); Log.v("myUri", String.valueOf(result)); try { // 替换fromFilePath为Bitmap方式创建InputImage Bitmap bitmap = getBitmapFromUri(result); InputImage image = InputImage.fromBitmap(bitmap, 0); // 已处理Exif方向,旋转角度传0 scanner.process(image) .addOnSuccessListener(this::processResult) .addOnFailureListener(e -> { Toast.makeText(MainActivity.this, "Failed to scan.", Toast.LENGTH_SHORT).show(); e.printStackTrace(); }); // 回收Bitmap避免内存泄漏 if (bitmap != null && !bitmap.isRecycled()) { bitmap.recycle(); } } catch (IOException e) { e.printStackTrace(); Toast.makeText(MainActivity.this, "Failed to load image.", Toast.LENGTH_SHORT).show(); } });
额外注意事项
- 确保已申请
READ_EXTERNAL_STORAGE权限,Android 6及以上版本需要动态申请,否则无法读取图库图片的Exif信息和真实路径。 - 针对大尺寸图片,建议在
getBitmapFromUri中添加Bitmap压缩逻辑,避免内存溢出(OOM)。
内容的提问来源于stack exchange,提问作者Yatik
相关产品推荐
相关产品推荐

