API 33设备使用ExifInterface读取图片位置元数据失败求助
问题分析与解决方案
1. 权限申请时机错误(核心问题)
你在选择图片后立刻发起权限申请,然后同步调用readLocationMetadata,但RequestPermission是异步操作——此时权限还未被用户授予,contentResolver.openInputStream实际没有读取权限,导致ExifInterface无法正确读取元数据,最终得到无效的0.0值。
修正权限逻辑
将读取元数据的操作移到权限申请的回调中,确保只有权限通过后才执行读取:
val requestPermissionLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.RequestPermission(), onResult = { isGranted -> if (isGranted) { // 权限通过后再读取元数据 uris.firstOrNull()?.let { uri -> val location = readLocationMetadata(context, uri) location?.let { viewModel.onEvent(AddSpotUIEvent.UpdateSpotLocation(it)) } } } } ) val multiplePhotoPickerLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.PickMultipleVisualMedia(maxItems = MAX_IMAGES_SELECTED), onResult = { uris -> viewModel.onEvent(AddSpotUIEvent.UpdateSelectedImages(uris)) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && uris.isNotEmpty()) { // 仅当有选中图片时申请权限 requestPermissionLauncher.launch(android.Manifest.permission.READ_MEDIA_IMAGES) } } )
2. 优化Exif元数据读取逻辑
你当前的代码只是获取了Exif的原始字符串,没有解析为十进制经纬度,且可以使用ExifInterface提供的便捷方法简化操作:
方案一:使用getLatLong方法(推荐)
ExifInterface自带getLatLong方法,可直接解析出经纬度,无需手动处理字符串:
fun readLocationMetadata(context: Context, imageUri: Uri): LatLng? { return try { context.contentResolver.openInputStream(imageUri)?.use { stream -> val exifInterface = ExifInterface(stream) val latLong = FloatArray(2) // 尝试读取经纬度,成功则返回LatLng if (exifInterface.getLatLong(latLong)) { LatLng(latLong[0].toDouble(), latLong[1].toDouble()) } else { null } } } catch (e: Exception) { e.printStackTrace() null } }
方案二:手动解析原始Exif字符串(如需自定义处理)
如果需要手动解析TAG_GPS_LATITUDE和TAG_GPS_LONGITUDE的字符串格式(如"39/1, 59/1, 1234/100"),可添加解析逻辑:
private fun parseExifCoordinate(coordinateStr: String?, refStr: String?): Double? { if (coordinateStr.isNullOrEmpty() || refStr.isNullOrEmpty()) return null val parts = coordinateStr.split(",").map { it.trim() } if (parts.size != 3) return null // 解析度分秒为十进制:(度 + 分/60 + 秒/3600),根据方向调整正负 val degrees = parts[0].split("/").let { it[0].toDouble() / it[1].toDouble() } val minutes = parts[1].split("/").let { it[0].toDouble() / it[1].toDouble() } val seconds = parts[2].split("/").let { it[0].toDouble() / it[1].toDouble() } var decimal = degrees + minutes / 60 + seconds / 3600 // 南纬/西经取负值 if (refStr == "S" || refStr == "W") { decimal *= -1 } return decimal } // 在readLocationMetadata中调用 fun readLocationMetadata(context: Context, imageUri: Uri): LatLng? { return try { context.contentResolver.openInputStream(imageUri)?.use { stream -> val exifInterface = ExifInterface(stream) val latitude = parseExifCoordinate( exifInterface.getAttribute(ExifInterface.TAG_GPS_LATITUDE), exifInterface.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF) ) val longitude = parseExifCoordinate( exifInterface.getAttribute(ExifInterface.TAG_GPS_LONGITUDE), exifInterface.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF) ) if (latitude != null && longitude != null) { LatLng(latitude, longitude) } else { null } } } catch (e: Exception) { e.printStackTrace() null } }
3. 额外排查点
- 确保测试图片确实包含有效的GPS元数据:可通过电脑端的图片属性(如Windows右键→属性→详细信息,或Mac的预览工具→工具→显示检查器→GPS)验证。
- 若仍无法读取,尝试使用
AssetFileDescriptor创建ExifInterface:context.contentResolver.openAssetFileDescriptor(imageUri, "r")?.use { fd -> val exifInterface = ExifInterface(fd.fileDescriptor) // 后续读取逻辑不变 }
内容的提问来源于stack exchange,提问作者Adria Fernandez
相关产品推荐
相关产品推荐

