Android 10:通过MediaStore获取带位置信息的相册

11

查看Android 10中引入的存储访问更改这里,位置信息现在默认被打上了黑色标记。

Google要求我们在“MediaStore”对象上调用setRequireOriginal(),并将媒体的uri作为参数。当你逐个获取媒体文件时,这是有效的,但是当我们查询整个相册的ContentResolver时呢?

可以参考这个示例:

String[] projection = {
        MediaStore.Files.FileColumns._ID,
        MediaStore.Files.FileColumns.DATA,
        MediaStore.Files.FileColumns.MEDIA_TYPE,
        MediaStore.Images.Media.DATE_TAKEN,
        MediaStore.Images.Media.WIDTH,
        MediaStore.Images.Media.HEIGHT,
        MediaStore.Images.Media.LATITUDE, // <----- THIS
        MediaStore.Images.Media.LONGITUDE, // <----- THIS
        MediaStore.Images.Media.MIME_TYPE,
};

String selection = MediaStore.Files.FileColumns.MEDIA_TYPE + "="
        + MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE;

Uri queryUri = MediaStore.Files.getContentUri("external");

Cursor cursor = null;
MediaStore a ;

try {
    cursor = context.getContentResolver().query(queryUri, projection, selection,
            null, MediaStore.Images.Media.DATE_TAKEN + " DESC");


}
catch (NullPointerException ex){
}

在开始使用Q时,纬度和经度始终设置为0。假设在清单文件中添加了ACCESS_MEDIA_LOCATION权限,是否有一种方法来获取一批媒体的位置数据?


我的图片在Android Q上无法显示,但在旧版本上可以显示,请问是否有新的权限? - Mateen Chaudhry
1个回答

11

不幸的是,MediaStore.Images.Media.LATITUDE和MediaStore.Images.Media.LONGITUDE在Android Q中已被弃用

解决方法是使用ExifInterface,如下所示:

Cursor cursor = null;
try {
    String[] projection = {
            MediaStore.Files.FileColumns._ID,
            MediaStore.Images.Media.DATE_TAKEN,
            MediaStore.Images.Media.WIDTH,
            MediaStore.Images.Media.HEIGHT,
            MediaStore.MediaColumns.TITLE,
            MediaStore.Images.Media.MIME_TYPE,
            MediaStore.Images.Media.LATITUDE,
            MediaStore.Images.Media.LONGITUDE
    };
    cursor = getContentResolver().query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            projection,
            null,
            null,
            MediaStore.Images.Media.DATE_TAKEN + " DESC");


    int idColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID);
    int titleColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.TITLE);
    int latColumn = BuildCompat.isAtLeastQ() ? -1
            : cursor.getColumnIndexOrThrow(MediaStore.Images.Media.LATITUDE);
    int longColumn = BuildCompat.isAtLeastQ() ? -1
            : cursor.getColumnIndexOrThrow(MediaStore.Images.Media.LONGITUDE);
    while (cursor.moveToNext()) {

        Uri photoUri = Uri.withAppendedPath(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                cursor.getString(idColumn));
        String title = cursor.getString(titleColumn);

        final double[] latLong;
        if (BuildCompat.isAtLeastQ()) {
            photoUri = MediaStore.setRequireOriginal(photoUri);
            InputStream stream = getContentResolver().openInputStream(photoUri);
            if (stream == null) {
                Log.w(TAG, "Got a null input stream for " + photoUri);
                continue;
            }

            ExifInterface exifInterface = new ExifInterface(stream);
            double[] returnedLatLong = exifInterface.getLatLong();
            // If it returns null, fall back to {0.0, 0.0}.
            latLong = returnedLatLong != null ? returnedLatLong : new double[2];

            // After using ExifInterface, the stream should not be reused.
            stream.close();
        } else {
            latLong = new double[]{
                    cursor.getFloat(latColumn),
                    cursor.getFloat(longColumn)
            };
        }

        Log.i(TAG, title + " | lat: " + latLong[0] + " | lng: " + latLong[1]);
    }
} catch (NullPointerException | IOException ex) {
    Log.e(TAG, "Caught exception", ex);
} finally {
    if (cursor != null) {
        cursor.close();
    }
}

目前这是在Android Q上获取照片经纬度的唯一方法。

这需要获得ACCESS_MEDIA_LOCATION权限。注意,此权限不会在“设置界面中可见”(来源),这意味着用户不会看到弹出窗口请求权限,尽管这是运行时权限。 这意味着您必须在运行时请求权限(与仅在清单文件中)但用户无需同意。之所以要添加这里是因为您可能想知道为什么没有显示额外的UI弹出窗口。


@PerracoLabs 很好的建议。谢谢你。我已经更新了代码,使用了像你建议的androidx库,并放置了适当的API级别检查。它仍然假定调用者持有“ACCESS_MEDIA_LOCATION”,但我认为这没问题。 - Nikki Borrelli
1
我在 https://issuetracker.google.com/issues/128675076 创建了一个问题,解释了一个查询媒体的陷阱,即一次只查询一个,这基本上意味着获取相同图库的性能降低了180倍。如果谷歌将来没有做出任何更改,我会接受你的回答,但我仍然希望他们能够创建一种获取具有位置信息的多个媒体的方式。 - Gauthier
1
如果应用程序的目标是Android Q,则需要请求READ_MEDIA_IMAGES以获取图像,READ_MEDIA_VIDEO以获取视频和READ_MEDIA_AUDIO以获取音频内容。@MateenChaudhry - Nikki Borrelli
1
@user1809913 - ACCESS_MEDIA_LOCATION 被认为是一项“危险”的权限,因此应用程序必须在运行时请求它(就像请求 READ_EXTERNAL_STORAGE 权限一样)。 - Nikki Borrelli
似乎 READ_MEDIA_IMAGESREAD_MEDIA_VIDEOREAD_MEDIA_AUDIO 已被移除,因为其他网站上引用它们的链接已经“失效”:这里这里这里 - Ben Butterworth
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接