在 Android Q 中替换 ContentResolver 查询中的“GROUP BY”(Android 10、API 29 更改)

whe*_*max 2 mediastore android-contentresolver android-sqlite android-10.0

我正在升级一些旧版本以针对 Android Q,当然此代码停止工作:

       String[] PROJECTION_BUCKET = {MediaStore.Images.ImageColumns.BUCKET_ID,
            MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
            MediaStore.Images.ImageColumns.DATE_TAKEN,
            MediaStore.Images.ImageColumns.DATA,
            "COUNT(" + MediaStore.Images.ImageColumns._ID + ") AS COUNT",
            MediaStore.Files.FileColumns.MEDIA_TYPE,
            MediaStore.MediaColumns._ID};

        String BUCKET_GROUP_BY = " 1) and " + BUCKET_WHERE.toString() + " GROUP BY 1,(2";

        cur = context.getContentResolver().query(images, PROJECTION_BUCKET,
            BUCKET_GROUP_BY, null, BUCKET_ORDER_BY);
Run Code Online (Sandbox Code Playgroud)

android.database.sqlite.SQLiteException:“GROUP”附近:语法错误(代码 1 SQLITE_ERROR[1])

这里它应该获取包含相册名称、日期、图片数量的图像列表 - 每个相册一张图像,因此我们可以创建相册选择器屏幕,而无需查询所有图片并循环遍历它以创建相册。

由于 SQL 查询停止工作,是否可以使用 contentResolver 对查询结果进行分组?

(我知道 ImageColumns.DATA 和“COUNT() AS COUNT”也被弃用,但这是一个关于 GROUP BY 的问题)

(有一种方法可以查询相册并单独查询照片,以获得相册封面的照片uri,但我想避免开销)

Per*_*abs 5

不幸的是,Android 10 及更高版本不再支持Group By ,也不再支持COUNT等任何聚合函数。这是设计使然,没有解决方法。

解决方案是您实际上想要避免的,即查询、迭代和获取指标。

首先,您可以使用下一个片段,它将解析存储桶(相册)以及每个存储桶中的记录数量。

我没有添加代码来解析缩略图,但很简单。您必须对所有相册实例中的每个存储桶 ID 执行查询,并使用第一条记录中的图像。

public final class AlbumQuery
{
    @NonNull
    public static HashMap<String, AlbumQuery.Album> get(@NonNull final Context context)
    {
        final HashMap<String, AlbumQuery.Album> output     = new HashMap<>();
        final Uri                               contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;

        final String[] projection = {MediaStore.Images.Media.BUCKET_DISPLAY_NAME, MediaStore.Images.Media.BUCKET_ID};

        try (final Cursor cursor = context.getContentResolver().query(contentUri, projection, null, null, null))
        {
            if ((cursor != null) && (cursor.moveToFirst() == true))
            {
                final int columnBucketName = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_DISPLAY_NAME);
                final int columnBucketId   = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_ID);

                do
                {
                    final String bucketId   = cursor.getString(columnBucketId);
                    final String bucketName = cursor.getString(columnBucketName);

                    if (output.containsKey(bucketId) == false)
                    {
                        final int              count = AlbumQuery.getCount(context, contentUri, bucketId);
                        final AlbumQuery.Album album = new AlbumQuery.Album(bucketId, bucketName, count);
                        output.put(bucketId, album);
                    }

                } while (cursor.moveToNext());
            }
        }

        return output;
    }

    private static int getCount(@NonNull final Context context, @NonNull final Uri contentUri, @NonNull final String bucketId)
    {
        try (final Cursor cursor = context.getContentResolver().query(contentUri,
                null, MediaStore.Images.Media.BUCKET_ID + "=?", new String[]{bucketId}, null))
        {
            return ((cursor == null) || (cursor.moveToFirst() == false)) ? 0 : cursor.getCount();
        }
    }

    public static final class Album
    {
        @NonNull
        public final String buckedId;
        @NonNull
        public final String bucketName;
        public final int    count;

        Album(@NonNull final String bucketId, @NonNull final String bucketName, final int count)
        {
            this.buckedId = bucketId;
            this.bucketName = bucketName;
            this.count = count;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)