实现 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上测试通过

相关推荐
安东尼肉店8 小时前
Android compose屏幕适配终极解决方案
android
2501_916007478 小时前
HTTPS 抓包乱码怎么办?原因剖析、排查步骤与实战工具对策(HTTPS 抓包乱码、gzipbrotli、TLS 解密、iOS 抓包)
android·ios·小程序·https·uni-app·iphone·webview
feiyangqingyun10 小时前
基于Qt和FFmpeg的安卓监控模拟器/手机摄像头模拟成onvif和28181设备
android·qt·ffmpeg
用户20187928316714 小时前
ANR之RenderThread不可中断睡眠state=D
android
煤球王子14 小时前
简单学:Android14中的Bluetooth—PBAP下载
android
小趴菜822714 小时前
安卓接入Max广告源
android
齊家治國平天下14 小时前
Android 14 系统 ANR (Application Not Responding) 深度分析与解决指南
android·anr
ZHANG13HAO14 小时前
Android 13.0 Framework 实现应用通知使用权默认开启的技术指南
android
【ql君】qlexcel14 小时前
Android 安卓RIL介绍
android·安卓·ril
写点啥呢14 小时前
android12解决非CarProperty接口深色模式设置后开机无法保持
android·车机·aosp·深色模式·座舱