java - IllegalArgumentException:列 DISTINCT bucket_display_name 无效
问题描述
我正在检索不同文件夹列表的列表,其中包含每个文件夹中包含多个视频的视频文件,这在具有 Android P 及更低版本的设备中运行良好,但是当我在具有 Android Q 的设备上运行时,应用程序崩溃。如何使其适用于运行 Android Q 的设备
java.lang.IllegalArgumentException:无效列 DISTINCT bucket_display_name
日志猫:
java.lang.IllegalArgumentException: Invalid column DISTINCT bucket_display_name
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:170)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:140)
at android.content.ContentProviderProxy.query(ContentProviderNative.java:423)
at android.content.ContentResolver.query(ContentResolver.java:944)
at android.content.ContentResolver.query(ContentResolver.java:880)
at android.content.ContentResolver.query(ContentResolver.java:836)
at com.aisar.mediaplayer.fragments.VideoFolderFragment$MediaQuery.getAllVideo(VideoFolderFragment.java:364)
at com.aisar.mediaplayer.fragments.VideoFolderFragment$VideosLoader.loadVideos(VideoFolderFragment.java:434)
at com.aisar.mediaplayer.fragments.VideoFolderFragment$VideosLoader.access$1100(VideoFolderFragment.java:413)
at com.aisar.mediaplayer.fragments.VideoFolderFragment$5.run(VideoFolderFragment.java:189)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:289)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
我的代码:
public class MediaQuery {
private Context context;
private int count = 0;
private Cursor cursor;
List<ModelVideoFolder> videoItems;
public MediaQuery(Context context) {
this.context = context;
}
public List<ModelVideoFolder> getAllVideo(String query) {
String selection = null;
String[] projection = {
"DISTINCT " + MediaStore.Video.Media.BUCKET_DISPLAY_NAME,
MediaStore.Video.Media.BUCKET_ID
};
cursor = context.getContentResolver().query(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
projection,
selection,
null,
query);
videoItems = new ArrayList<>();
ModelVideoFolder videoItem;
while (cursor.moveToNext()) {
videoItem = new ModelVideoFolder(
"" + cursor.getString(1),
"" + cursor.getString(0),
"",
"",
"" + getVideosCount(cursor.getString(1))
);
videoItems.add(videoItem);
}
return videoItems;
}
public int getVideosCount(String BUCKET_ID) {
int count = 0;
String selection = null;
String[] projection = {
MediaStore.Video.Media.BUCKET_ID,
};
Cursor cursor = getActivity().getContentResolver().query(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
projection,
selection,
null,
null);
while (cursor.moveToNext()) {
if (BUCKET_ID.equals(cursor.getString(0))) {
//add only those videos that are in selected/chosen folder
count++;
}
}
return count;
}
}
解决方案
这是由于 Android Q 中的限制。
在 Android Q中,投影必须仅包含有效的列名,而无需附加语句。无法再在投影中嵌入任何类型的 SQL 语句。
因此,诸如“DISTINCT”+ YourColumName 之类的投影,甚至尝试创建诸如“ExistingColumnName AS AnotherName ”之类的列别名总是会失败。
解决方法是执行多个查询(游标)以获取所需的指标,并使用结果构造CursorWrapper或MatrixCursor。
请参阅下一个问题链接,其中说明了此行为,因为它是 Q 中改进的存储安全模型的一部分:
https://issuetracker.google.com/issues/130965914
对于您的具体问题,解决方案可能如下:
首先查询游标,获取所有视频所在的 BUCKET_ID 值列表。在选择中,您可以使用 MediaStore.Files.FileColumns.MEDIA_TYPE = MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO 过滤以仅定位视频文件
使用检索到的游标,迭代所有 BUCKET_ID 值以对每个存储桶执行单独的查询并检索视频记录,您可以从中解析计数。在迭代时跟踪每个 BUCKET_ID 并跳过任何已查询的内容。并且不要忘记也执行相同的 MEDIA_TYPE 过滤器选择,以避免查询可能驻留在同一存储桶中的非视频文件。
尝试基于您的问题代码的下一个片段,我尚未对其进行测试,但您可能会了解如何继续:
public static class MediaQuery
{
@NonNull
public static HashMap<String, ModelVideoFolder> get(@NonNull final Context context)
{
final HashMap<String, ModelVideoFolder> output = new HashMap<>();
final Uri contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
final String[] projection = {MediaStore.Video.Media.BUCKET_DISPLAY_NAME,
MediaStore.Video.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.Video.Media.BUCKET_DISPLAY_NAME);
final int columnBucketId = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.BUCKET_ID);
do
{
final String bucketName = cursor.getString(columnBucketName);
final String bucketId = cursor.getString(columnBucketId);
if (output.containsKey(bucketId) == false)
{
final int count = MediaQuery.getCount(context, contentUri, bucketId);
final ModelVideoFolder item = new ModelVideoFolder(
bucketName, bucketId, null, null, count);
output.put(bucketId, item);
}
} 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.Video.Media.BUCKET_ID + "=?", new String[]{bucketId}, null))
{
return ((cursor == null) || (cursor.moveToFirst() == false)) ? 0 : cursor.getCount();
}
}
}
推荐阅读
- jquery - 使用 jQuery 从其他函数中检查函数内部的真/假
- java - MongoDB Inc. 的异步驱动程序 (java) 是否通过连接多路复用请求?
- c++ - ROS环境中的构建错误(catkin)
- c# - 关于设置 DataGrid DataContext 的 ArgumentOutOfRange
- java - 如何填充动态 XFA 表单(itext 7)的动态内容?
- generics - Kotlin:通用变量
- r - 运行 r 脚本后退出命令提示符
- firebase - 需要存储在 Google Database 中的 python 文件在 Google Cloud Engine 中编译并将数据返回到 IOS 应用程序
- laravel - Laravel where 行为异常
- java - 如何在 React 中跟踪用户?