android-sqlite - 替换 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 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;
}
}
}
推荐阅读
- node.js - 使用聚合对 MongoDB 列求和 - 结果没有求和的属性
- android - 确保 30 分钟后不缓存前台服务
- linux - Yocto bitbake 包组
- sql - De Morgan 定律如何在条件 SQL 查询中起作用?
- java - 在android WebView中完成页面加载后如何获取和使用当前url
- javascript - Uint8ClampedArray 到 Uint8Array 到 base64
- c++ - 比 std::atomic 更便宜的替代品
? - javascript - Discord.js v12 TypeError:无法读取未定义的属性“hasPermission”
- r - ggplot:为什么我必须将数据转换为长格式?
- javascript - Watcher 内 Vue 类组件装饰器的访问方法