实现 Android 图片信息获取和 EXIF 坐标解析

1、权限:

bash 复制代码
	<!-- Android 13(API 33)及以上,允许应用读取存储在外部存储上的图片文件。替代了:READ_EXTERNAL_STORAGE(在 Android 13 被细化成 READ_MEDIA_IMAGES、READ_MEDIA_VIDEO、READ_MEDIA_AUDIO 等) -->
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    
    <!-- Android 12及以下 允许应用读取外部存储器(SD卡或主存储空间)中的所有文件(不区分图片、音频、视频等) -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    
    <!-- 允许应用读取媒体文件(如图片、视频)中嵌入的位置信息(GPS坐标),这部分信息通常存在于 EXIF 元数据中。 -->
    <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />

2、动态申请权限:

kotlin 复制代码
private val REQUEST_CODE_MEDIA = 1001

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    requestMediaPermissions(this, REQUEST_CODE_MEDIA)
}

fun requestMediaPermissions(activity: Activity, requestCode: Int) {
    val permissions = mutableListOf<String>()

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        // Android 13+
        permissions.add(android.Manifest.permission.READ_MEDIA_IMAGES)
    } else {
        // Android 12 及以下
        permissions.add(android.Manifest.permission.READ_EXTERNAL_STORAGE)
    }

    // Android 10+ 支持读取媒体位置信息
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        permissions.add(android.Manifest.permission.ACCESS_MEDIA_LOCATION)
    }

    ActivityCompat.requestPermissions(activity, permissions.toTypedArray(), requestCode)
}

override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<String>,
    grantResults: IntArray
) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)

    if (requestCode == REQUEST_CODE_MEDIA) {
        if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
            Log.d("Permission", "所有权限已授予")
            // 可以开始读取图片及其位置信息
        } else {
            Log.e("Permission", "部分权限被拒绝,功能可能受限")
        }
    }
}

3、获取图片信息(路径和经纬度)代码实现:

kotlin 复制代码
import android.content.ContentUris
import android.content.Context
import androidx.exifinterface.media.ExifInterface
import android.net.Uri
import android.provider.MediaStore
import android.util.Log
import com.next.homestorage.entity.ImageInfo

/**
  * 获取系统图片信息列表
  */
fun getSystemImageInfos(context: Context): List<ImageInfo> {
    val imageList = mutableListOf<ImageInfo>()

    val projection = arrayOf(
        MediaStore.Images.Media._ID, //系统为每一张图片生成的唯一 ID
        MediaStore.Images.Media.DATA, //图片在设备本地文件系统中的实际路径
        MediaStore.Images.Media.DISPLAY_NAME //图片的文件显示名称(不包括路径)
    )
    val uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
    val sortOrder = "${MediaStore.Images.Media.DATE_ADDED} DESC"

    context.contentResolver.query(uri, projection, null, null, sortOrder)?.use { cursor ->
        val columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
        val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)

        while (cursor.moveToNext()) {
            val id = cursor.getLong(idColumn)
            val contentUri = ContentUris.withAppendedId(uri, id)

            val (lat, lon) = readImageLocation(context, contentUri)
            val path = cursor.getString(columnIndex)
            val imageInfo = ImageInfo( path, lat, lon)
            imageList.add(imageInfo)

            Log.d("MediaFile", "path=$path")
            Log.d("MediaFile", "uri=$contentUri")
            Log.d("MediaFile", "uriPath=${contentUri.path}")
        }
    }
    return imageList
}

/**
 * 获取图片经纬度
 */
private fun readImageLocation(context: Context, uri: Uri): Pair<Float, Float> {
    var latitude = 0f
    var longitude = 0f
    try {
        context.contentResolver.openInputStream(uri)?.use { input ->
            val exif = ExifInterface(input)
            val latLong = FloatArray(2)
            exif.latLong?.let {
                latitude = it[0].toFloat()
                longitude = it[1].toFloat()
                Log.d("MediaFile", "EXIF Lat: ${latLong[0]}, Lon: ${latLong[1]}")
            }
        }
    } catch (e: Exception) {
        Log.e("MediaFile", "Error reading EXIF for $uri", e)
    }
    return latitude to longitude
}

data class ImageInfo(
    val path: String,
    val latitude: Float,
    val longitude: Float
)

注:代码已在Android 12上测试通过

相关推荐
似霰30 分钟前
安卓adb shell串口基础指令
android·adb
fatiaozhang95273 小时前
中兴云电脑W102D_晶晨S905X2_2+16G_mt7661无线_安卓9.0_线刷固件包
android·adb·电视盒子·魔百盒刷机·魔百盒固件
CYRUS_STUDIO4 小时前
Android APP 热修复原理
android·app·hotfix
鸿蒙布道师4 小时前
鸿蒙NEXT开发通知工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
鸿蒙布道师4 小时前
鸿蒙NEXT开发网络相关工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
大耳猫4 小时前
【解决】Android Gradle Sync 报错 Could not read workspace metadata
android·gradle·android studio
dpxiaolong6 小时前
RK3588平台用v4l工具调试USB摄像头实践(亮度,饱和度,对比度,色相等)
android·windows
tangweiguo030519877 小时前
Android 混合开发实战:统一 View 与 Compose 的浅色/深色主题方案
android
老狼孩111227 小时前
2025新版懒人精灵零基础及各板块核心系统视频教程-全分辨率免ROOT自动化开发
android·机器人·自动化·lua·脚本开发·懒人精灵·免root开发