Android 10+使用ExifInterface读取存储图片GPS经纬度失败问题
问题分析与解决方案
核心问题
当选择content://com.android.externalstorage.documents/类型的存储目录图片时,直接通过openInputStream()拿到的是媒体库提供的剥离GPS元数据的文件副本(Android 10+ Scoped Storage机制默认行为),导致无法读取GPS经纬度,其他EXIF数据不受影响。
具体修复步骤
1. 强制请求原始文件URI
API 29(Android 10)及以上,必须调用MediaStore.setRequireOriginal(uri)获取包含完整EXIF的原始文件,需处理不支持该方法的异常场景:
Uri selecteduri = data.getData(); // 关键:请求原始文件URI(仅API 29+生效) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { try { selecteduri = MediaStore.setRequireOriginal(selecteduri); } catch (UnsupportedOperationException e) { // 处理第三方文件管理器返回的不兼容URI e.printStackTrace(); } }
2. 修复权限申请逻辑
ACCESS_MEDIA_LOCATION是API 29新增权限,需单独做版本判断,且需批量申请所有必要权限:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); List<String> permissionsToRequest = new ArrayList<>(); // 申请READ_EXTERNAL_STORAGE if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { permissionsToRequest.add(Manifest.permission.READ_EXTERNAL_STORAGE); } // API 29+ 申请ACCESS_MEDIA_LOCATION if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && checkSelfPermission(Manifest.permission.ACCESS_MEDIA_LOCATION) != PackageManager.PERMISSION_GRANTED) { permissionsToRequest.add(Manifest.permission.ACCESS_MEDIA_LOCATION); } if (!permissionsToRequest.isEmpty()) { // 批量申请权限 registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), result -> { boolean allGranted = true; for (Boolean granted : result.values()) { if (!granted) { allGranted = false; break; } } if (allGranted) { openImagePicker(); } }).launch(permissionsToRequest.toArray(new String[0])); } else { openImagePicker(); } }
3. 优化图片选择Intent
使用ACTION_OPEN_DOCUMENT替代ACTION_GET_CONTENT,更符合Scoped Storage设计,同时精简权限标记:
public void openImagePicker() { Intent photoPickerIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT); photoPickerIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); photoPickerIntent.setType("image/*"); photoPickerIntent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); someActivityResultLauncher.launch(Intent.createChooser(photoPickerIntent, "选择图片")); }
4. 修复流资源泄漏问题
将InputStream关闭逻辑放入finally块,避免异常时资源无法释放:
InputStream stream = null; try { stream = getContentResolver().openInputStream(selecteduri); if (stream != null) { ExifInterface exifInterface = new ExifInterface(stream); float[] output = new float[2]; boolean hasGps = exifInterface.getLatLong(output); if (hasGps) { System.out.println("Lat: " + output[0]); System.out.println("Long: " + output[1]); } else { System.out.println("无GPS数据"); } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { e.printStackTrace(); } } }
关键注意事项
- 禁止使用URI转文件路径的方法,Scoped Storage下该方式已被废弃且可能失效。
ACCESS_MEDIA_LOCATION是Android 10+读取GPS元数据的必要条件,必须与READ_EXTERNAL_STORAGE同时申请。
内容的提问来源于stack exchange,提问作者Uvin Abeysinghe
相关产品推荐
相关产品推荐

