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

相关推荐
雨白5 小时前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹7 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空8 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭9 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日10 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安10 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑10 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟14 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡15 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi0016 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体