首页 > 解决方案 > 替换 Android Q 中 ContentResolver 查询中的“GROUP BY”(Android 10,API 29 更改)

问题描述

我正在升级一些旧版以针对 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);

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

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

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

(我知道 ImageColumns.DATA 和 "COUNT() AS COUNT" 也已弃用,但这是关于 GROUP BY 的问题)

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

标签: android-sqliteandroid-contentresolvermediastoreandroid-10.0

解决方案


不幸的是,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;
        }
    }
}

推荐阅读