You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android 13中ExifInterface.getLatLong()始终返回false的解决咨询

Android 13下ExifInterface无法读取照片经纬度的解决方案

问题根源分析

  1. 权限校验升级:Android 13(API 33)新增ACCESS_MEDIA_LOCATION权限,即使拥有READ_MEDIA_IMAGES,也必须单独申请该权限才能读取EXIF中的定位信息。
  2. 分区存储限制:手动拼接file:// Uri的方式,在Android 10+的分区存储机制下,无法获取完整的文件访问权限,导致ExifInterface无法读取定位元数据。
  3. 原生方法兼容性:系统原生ExifInterface的getLatLong()方法在Android 13上存在兼容性问题,部分场景下无法正确解析GPS标签。

解决方案

1. 完善权限配置

在AndroidManifest.xml中添加对应权限声明:

<!-- Android 13+ 媒体位置权限 -->
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
<!-- Android 13+ 图片读取权限 -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<!-- Android 10-12 存储读取权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />

2. 适配分区存储的Uri获取方式

避免手动拼接file:// Uri,改用MediaStore查询文件对应的内容Uri:

private fun getContentUriFromPath(context: Context, filePath: String): Uri? {
    val projection = arrayOf(MediaStore.Images.Media._ID)
    val selection = "${MediaStore.Images.Media.DATA} = ?"
    val selectionArgs = arrayOf(filePath)
    
    context.contentResolver.query(
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
        projection,
        selection,
        selectionArgs,
        null
    )?.use { cursor ->
        if (cursor.moveToFirst()) {
            val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID))
            return ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id)
        }
    }
    return null
}

3. 使用AndroidX ExifInterface库(跨版本兼容)

引入AndroidX的ExifInterface库修复原生类的兼容性问题,在Module级build.gradle添加依赖:

implementation "androidx.exifinterface:exifinterface:1.3.6"

修改核心逻辑,手动解析GPS标签替代getLatLong()方法:

private fun getLocationAndMoveOn() {
    if (hasMediaLocationPermission(requireContext())) {
        val pickedPhotoData = libraryViewModel.viewState.pickedPhoto.value?.DATA ?: return
        val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            getContentUriFromPath(requireContext(), pickedPhotoData) ?: return
        } else {
            Uri.fromFile(File(pickedPhotoData))
        }

        requireContext().contentResolver.openInputStream(uri)?.use { stream ->
            androidx.exifinterface.media.ExifInterface(stream).run {
                // 读取EXIF中的GPS标签
                val latitude = getAttribute(ExifInterface.TAG_GPS_LATITUDE)
                val latitudeRef = getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF)
                val longitude = getAttribute(ExifInterface.TAG_GPS_LONGITUDE)
                val longitudeRef = getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF)

                if (!latitude.isNullOrEmpty() && !latitudeRef.isNullOrEmpty() && !longitude.isNullOrEmpty() && !longitudeRef.isNullOrEmpty()) {
                    // 转换为十进制经纬度
                    val lat = convertExifToDegree(latitude) * if (latitudeRef == "N") 1 else -1
                    val lon = convertExifToDegree(longitude) * if (longitudeRef == "E") 1 else -1
                    val location = Location("").apply {
                        latitude = lat
                        longitude = lon
                    }
                    navigateToLocation(location)
                } else {
                    showPhotoLocationDataDialog()
                }
            }
        }
    } else {
        requestMediaLocationPermission(requireActivity(), REQUEST_PERMISSION_MEDIA_ACCESS)
    }
}

// 将EXIF格式的经纬度转换为十进制度数
private fun convertExifToDegree(exifValue: String): Double {
    val parts = exifValue.split(",").map { it.split("/") }
    val degrees = parts[0][0].toDouble() / parts[0][1].toDouble()
    val minutes = parts[1][0].toDouble() / parts[1][1].toDouble()
    val seconds = parts[2][0].toDouble() / parts[2][1].toDouble()
    return degrees + (minutes / 60) + (seconds / 3600)
}

4. 优化权限检查逻辑

确保hasMediaLocationPermission()适配全版本:

private fun hasMediaLocationPermission(context: Context): Boolean {
    return when {
        Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> {
            ContextCompat.checkSelfPermission(context, Manifest.permission.READ_MEDIA_IMAGES) == PackageManager.PERMISSION_GRANTED &&
            ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_MEDIA_LOCATION) == PackageManager.PERMISSION_GRANTED
        }
        Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> {
            ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&
            ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_MEDIA_LOCATION) == PackageManager.PERMISSION_GRANTED
        }
        else -> {
            ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
        }
    }
}

内容的提问来源于stack exchange,提问作者Helios

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 20:03:21