android.database.sqlite.SQLiteException:没有这样的列:bucket_display_name
android.database.sqlite.SQLiteException: no such column: bucket_display_name
我正在尝试使用 MediaStore
获取包含音频文件的存储桶。这在 Android 10 API 29 上工作正常,但不适用于以前的 Android 版本。我在 Android 10 API 29.
上附上了工作示例的屏幕截图
Caused by: android.database.sqlite.SQLiteException: no such column:
bucket_display_name (code 1 SQLITE_ERROR): , while compiling: SELECT
bucket_display_name, bucket_id FROM audio ORDER BY date_added ASC
logcat.
Caused by: android.database.sqlite.SQLiteException: no such column: bucket_display_name (code 1 SQLITE_ERROR): , while compiling: SELECT bucket_display_name, bucket_id FROM audio ORDER BY date_added ASC
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:179)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:135)
at android.content.ContentProviderProxy.query(ContentProviderNative.java:418)
at android.content.ContentResolver.query(ContentResolver.java:802)
at android.content.ContentResolver.query(ContentResolver.java:752)
at android.content.ContentResolver.query(ContentResolver.java:710)
at com.aisar.mediaplayer.fragments.AudioFolderFragment$AsyncVideoFolderLoader.doInBackground(AudioFolderFragment.java:148)
at com.aisar.mediaplayer.fragments.AudioFolderFragment$AsyncVideoFolderLoader.doInBackground(AudioFolderFragment.java:130)
at android.os.AsyncTask.call(AsyncTask.java:333)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.AsyncTask$SerialExecutor.run(AsyncTask.java:245)
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:764)
代码:
class AsyncVideoFolderLoader extends AsyncTask<String, String, List<ModelAudioFolder>> {
private String sortBy;
public AsyncVideoFolderLoader(String sortBy) {
this.sortBy = sortBy;
}
@Override
protected List<ModelAudioFolder> doInBackground(String... strings) {
List<ModelAudioFolder> videoItems = new ArrayList<>();
videoItems.clear();
final HashMap<String, ModelAudioFolder> output = new HashMap<>();
final Uri contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
final String[] projection = {MediaStore.Audio.Media.BUCKET_DISPLAY_NAME, MediaStore.Audio.Media.BUCKET_ID};
try (final Cursor cursor = getActivity().getContentResolver().query(
contentUri,
projection,
null,
null,
"" + sortBy)) {
if ((cursor != null) && (cursor.moveToFirst())) {
final int columnBucketName = cursor.getColumnIndex(MediaStore.Audio.Media.BUCKET_DISPLAY_NAME);
final int columnBucketId = cursor.getColumnIndex(MediaStore.Audio.Media.BUCKET_ID);
do {
final String bucketName = cursor.getString(columnBucketName);
final String bucketId = cursor.getString(columnBucketId);
if (!output.containsKey(bucketId)) {
final int count = getCount(contentUri, bucketId);
final ModelAudioFolder item = new ModelAudioFolder(
"" + bucketId,
"" + bucketName,
"",
"" + getPath(bucketId),
"" + count
);
output.put(bucketId, item);
videoItems.add(item);
}
} while (cursor.moveToNext());
}
}
return videoItems;
}
private int getCount(@NonNull final Uri contentUri, @NonNull final String bucketId) {
try (final Cursor cursor = getActivity().getContentResolver().query(contentUri,
null, MediaStore.Audio.Media.BUCKET_ID + "=?", new String[]{bucketId}, null)) {
return ((cursor == null) || (cursor.moveToFirst() == false)) ? 0 : cursor.getCount();
}
}
private String getPath(String BUCKET_ID) {
String path = "";
String selection = null;
String[] projection = {
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.BUCKET_ID
};
Cursor cursor = getActivity().getContentResolver().query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
projection,
selection,
null,
null);
while (cursor.moveToNext()) {
if (BUCKET_ID.equals(cursor.getString(1))) {
//add only those videos that are in selected/chosen folder
path = cursor.getString(0);
}
}
return path;
}
@Override
protected void onPostExecute(List<ModelAudioFolder> audioFolderList) {
super.onPostExecute(audioFolderList);
if (audioFolderList.size() <= 0) {
noFoldersRl.setVisibility(View.VISIBLE);
foldersRl.setVisibility(View.GONE);
} else {
noFoldersRl.setVisibility(View.GONE);
foldersRl.setVisibility(View.VISIBLE);
}
Log.d("FoldersSize", "onPostExecute: " + audioFolderList.size());
adapterAudioFolder = new AdapterAudioFolder(getActivity(), audioFolderList, dashboardActivity);
foldersRv.setAdapter(adapterAudioFolder);
}
}
...
异常的原因是BUCKET_DISPLAY_NAME
。它是在 API 29 中添加的。在此之前,我们对 API 28 及以下版本使用 DISPLAY_NAME
。请参考文档 BUCKET_DISPLAY_NAME.
解决方案可以根据当前API级别写条件。要获取文件夹名称,您可以使用 RELATIVE_PATH
.
在 API 版本 <= 28 BUCKET_DISPLAY_NAME 中仅适用于图像和视频,因此可通过 MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME 或 MediaStore.Vidos.VideoColumns.BUCKET_DISPLAY_NAME 访问。
也就是说,在 29 以下的版本中为 audios 获取 BUCKET_DISPLAY_NAME 的解决方案是解析 MediaStore.Files.FileColumns.DATA。此字段在 29 中已弃用,但在以前的版本中仍然有效。
该字段包含实际路径+文件名,并且由于 BUCKET_DISPLAY_NAME 实际上是文件所在的实际 parent 文件夹名称,因此您需要做的是删除文件名,然后从路径中找到最后一个反斜杠开始的子字符串。
我现在自己做了。首先检索所有音频文件,然后分离音频 URI。从音频 URI,我得到了文件夹名称。我在这里发布我的解决方案,这样也许其他人可以从中受益。
class AsyncVideoFolderLoader extends AsyncTask<String, String, List<ModelAudioFolder>> {
private Cursor cursor;
List<ModelAudioTe> audioList;
private String sortBy;
public AsyncVideoFolderLoader(String sortBy) {
this.sortBy = sortBy;
}
@Override
protected List<ModelAudioFolder> doInBackground(String... strings) {
String selection = null;
String[] projection;
projection = new String[]{
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.SIZE,
MediaStore.Audio.Media.ALBUM_ID
};
cursor = getActivity().getContentResolver().query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
projection,
selection,
null,
null);
audioList = new ArrayList<>();
ModelAudioTe modelAudio;
while (cursor.moveToNext()) {
modelAudio = new ModelAudioTe(
"" + cursor.getString(0),
"" + cursor.getString(1),
"" + cursor.getString(2),
"" + cursor.getString(3),
"" + cursor.getString(4),
"" + cursor.getString(5),
"" + cursor.getString(6),
"" + cursor.getString(7));
audioList.add(modelAudio);
}
//creating audio paths/uris list
ArrayList<String> pathsList = new ArrayList<>();
pathsList.clear();
for (int i = 0; i < audioList.size(); i++) {
String folderName = new File(audioList.get(i).getDATA()).getParentFile().getName();
String folderId = new File(audioList.get(i).getDATA()).getParentFile().getParent();
pathsList.add(folderId + "/" + folderName);
}
//generating folder names from audio paths/uris
List<ModelAudioFolder> folderList = new ArrayList<>();
folderList.clear();
for (int i = 0; i < audioList.size(); i++) {
String folderName = new File(audioList.get(i).getDATA()).getParentFile().getName();
String folderId = new File(audioList.get(i).getDATA()).getParentFile().getParent();
int count = Collections.frequency(pathsList, folderId + "/" + folderName);
String folderRoot;
String folderRoot1 = "";
if (audioList.get(i).getDATA().contains("emulated")) {
folderRoot = "emulated";
} else {
folderRoot = "storage";
}
if (i > 0) {
if (audioList.get(i - 1).getDATA().contains("emulated")) {
folderRoot1 = "emulated";
} else {
folderRoot1 = "storage";
}
}
if (i == 0) {
ModelAudioFolder model = new ModelAudioFolder("" + folderId + "/" + folderName, "" + folderName, "", "" + folderId + "/" + folderName, "" + count);
folderList.add(model);
Log.d("The_Tag1", "onCreate: " + folderName + " " + folderRoot + " " + folderId + "/" + folderName + " " + count);
} else if (
folderName.equals(new File(audioList.get(i - 1).getDATA()).getParentFile().getName())
&&
folderRoot.equals(folderRoot1)
) {
//exclude
} else {
ModelAudioFolder model = new ModelAudioFolder("" + folderId + "/" + folderName, "" + folderName, "", "" + folderId + "/" + folderName, "" + count);
folderList.add(model);
Log.d("The_Tag1", "onCreate: " + folderName + " " + folderRoot + " " + folderId + "/" + folderName + " " + " " + count);
}
}
return folderList;
}
@Override
protected void onPostExecute(List<ModelAudioFolder> audioFolderList) {
super.onPostExecute(audioFolderList);
Log.d("ModelAudioFolder_Size", "Count:" + audioFolderList.size());
try {
if (audioFolderList.size() <= 0) {
noFoldersRl.setVisibility(View.VISIBLE);
foldersRl.setVisibility(View.GONE);
} else {
noFoldersRl.setVisibility(View.GONE);
foldersRl.setVisibility(View.VISIBLE);
}
Log.d("FoldersSize", "onPostExecute: " + audioFolderList.size());
adapterAudioFolder = new AdapterAudioFolder(getActivity(), audioFolderList, dashboardActivity);
foldersRv.setAdapter(adapterAudioFolder);
} catch (Exception e) {
e.printStackTrace();
}
}
}
我正在尝试使用 MediaStore
获取包含音频文件的存储桶。这在 Android 10 API 29 上工作正常,但不适用于以前的 Android 版本。我在 Android 10 API 29.
Caused by: android.database.sqlite.SQLiteException: no such column: bucket_display_name (code 1 SQLITE_ERROR): , while compiling: SELECT bucket_display_name, bucket_id FROM audio ORDER BY date_added ASC
logcat.
Caused by: android.database.sqlite.SQLiteException: no such column: bucket_display_name (code 1 SQLITE_ERROR): , while compiling: SELECT bucket_display_name, bucket_id FROM audio ORDER BY date_added ASC
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:179)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:135)
at android.content.ContentProviderProxy.query(ContentProviderNative.java:418)
at android.content.ContentResolver.query(ContentResolver.java:802)
at android.content.ContentResolver.query(ContentResolver.java:752)
at android.content.ContentResolver.query(ContentResolver.java:710)
at com.aisar.mediaplayer.fragments.AudioFolderFragment$AsyncVideoFolderLoader.doInBackground(AudioFolderFragment.java:148)
at com.aisar.mediaplayer.fragments.AudioFolderFragment$AsyncVideoFolderLoader.doInBackground(AudioFolderFragment.java:130)
at android.os.AsyncTask.call(AsyncTask.java:333)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.AsyncTask$SerialExecutor.run(AsyncTask.java:245)
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:764)
代码:
class AsyncVideoFolderLoader extends AsyncTask<String, String, List<ModelAudioFolder>> {
private String sortBy;
public AsyncVideoFolderLoader(String sortBy) {
this.sortBy = sortBy;
}
@Override
protected List<ModelAudioFolder> doInBackground(String... strings) {
List<ModelAudioFolder> videoItems = new ArrayList<>();
videoItems.clear();
final HashMap<String, ModelAudioFolder> output = new HashMap<>();
final Uri contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
final String[] projection = {MediaStore.Audio.Media.BUCKET_DISPLAY_NAME, MediaStore.Audio.Media.BUCKET_ID};
try (final Cursor cursor = getActivity().getContentResolver().query(
contentUri,
projection,
null,
null,
"" + sortBy)) {
if ((cursor != null) && (cursor.moveToFirst())) {
final int columnBucketName = cursor.getColumnIndex(MediaStore.Audio.Media.BUCKET_DISPLAY_NAME);
final int columnBucketId = cursor.getColumnIndex(MediaStore.Audio.Media.BUCKET_ID);
do {
final String bucketName = cursor.getString(columnBucketName);
final String bucketId = cursor.getString(columnBucketId);
if (!output.containsKey(bucketId)) {
final int count = getCount(contentUri, bucketId);
final ModelAudioFolder item = new ModelAudioFolder(
"" + bucketId,
"" + bucketName,
"",
"" + getPath(bucketId),
"" + count
);
output.put(bucketId, item);
videoItems.add(item);
}
} while (cursor.moveToNext());
}
}
return videoItems;
}
private int getCount(@NonNull final Uri contentUri, @NonNull final String bucketId) {
try (final Cursor cursor = getActivity().getContentResolver().query(contentUri,
null, MediaStore.Audio.Media.BUCKET_ID + "=?", new String[]{bucketId}, null)) {
return ((cursor == null) || (cursor.moveToFirst() == false)) ? 0 : cursor.getCount();
}
}
private String getPath(String BUCKET_ID) {
String path = "";
String selection = null;
String[] projection = {
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.BUCKET_ID
};
Cursor cursor = getActivity().getContentResolver().query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
projection,
selection,
null,
null);
while (cursor.moveToNext()) {
if (BUCKET_ID.equals(cursor.getString(1))) {
//add only those videos that are in selected/chosen folder
path = cursor.getString(0);
}
}
return path;
}
@Override
protected void onPostExecute(List<ModelAudioFolder> audioFolderList) {
super.onPostExecute(audioFolderList);
if (audioFolderList.size() <= 0) {
noFoldersRl.setVisibility(View.VISIBLE);
foldersRl.setVisibility(View.GONE);
} else {
noFoldersRl.setVisibility(View.GONE);
foldersRl.setVisibility(View.VISIBLE);
}
Log.d("FoldersSize", "onPostExecute: " + audioFolderList.size());
adapterAudioFolder = new AdapterAudioFolder(getActivity(), audioFolderList, dashboardActivity);
foldersRv.setAdapter(adapterAudioFolder);
}
}
...
异常的原因是BUCKET_DISPLAY_NAME
。它是在 API 29 中添加的。在此之前,我们对 API 28 及以下版本使用 DISPLAY_NAME
。请参考文档 BUCKET_DISPLAY_NAME.
解决方案可以根据当前API级别写条件。要获取文件夹名称,您可以使用 RELATIVE_PATH
.
在 API 版本 <= 28 BUCKET_DISPLAY_NAME 中仅适用于图像和视频,因此可通过 MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME 或 MediaStore.Vidos.VideoColumns.BUCKET_DISPLAY_NAME 访问。
也就是说,在 29 以下的版本中为 audios 获取 BUCKET_DISPLAY_NAME 的解决方案是解析 MediaStore.Files.FileColumns.DATA。此字段在 29 中已弃用,但在以前的版本中仍然有效。
该字段包含实际路径+文件名,并且由于 BUCKET_DISPLAY_NAME 实际上是文件所在的实际 parent 文件夹名称,因此您需要做的是删除文件名,然后从路径中找到最后一个反斜杠开始的子字符串。
我现在自己做了。首先检索所有音频文件,然后分离音频 URI。从音频 URI,我得到了文件夹名称。我在这里发布我的解决方案,这样也许其他人可以从中受益。
class AsyncVideoFolderLoader extends AsyncTask<String, String, List<ModelAudioFolder>> {
private Cursor cursor;
List<ModelAudioTe> audioList;
private String sortBy;
public AsyncVideoFolderLoader(String sortBy) {
this.sortBy = sortBy;
}
@Override
protected List<ModelAudioFolder> doInBackground(String... strings) {
String selection = null;
String[] projection;
projection = new String[]{
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.SIZE,
MediaStore.Audio.Media.ALBUM_ID
};
cursor = getActivity().getContentResolver().query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
projection,
selection,
null,
null);
audioList = new ArrayList<>();
ModelAudioTe modelAudio;
while (cursor.moveToNext()) {
modelAudio = new ModelAudioTe(
"" + cursor.getString(0),
"" + cursor.getString(1),
"" + cursor.getString(2),
"" + cursor.getString(3),
"" + cursor.getString(4),
"" + cursor.getString(5),
"" + cursor.getString(6),
"" + cursor.getString(7));
audioList.add(modelAudio);
}
//creating audio paths/uris list
ArrayList<String> pathsList = new ArrayList<>();
pathsList.clear();
for (int i = 0; i < audioList.size(); i++) {
String folderName = new File(audioList.get(i).getDATA()).getParentFile().getName();
String folderId = new File(audioList.get(i).getDATA()).getParentFile().getParent();
pathsList.add(folderId + "/" + folderName);
}
//generating folder names from audio paths/uris
List<ModelAudioFolder> folderList = new ArrayList<>();
folderList.clear();
for (int i = 0; i < audioList.size(); i++) {
String folderName = new File(audioList.get(i).getDATA()).getParentFile().getName();
String folderId = new File(audioList.get(i).getDATA()).getParentFile().getParent();
int count = Collections.frequency(pathsList, folderId + "/" + folderName);
String folderRoot;
String folderRoot1 = "";
if (audioList.get(i).getDATA().contains("emulated")) {
folderRoot = "emulated";
} else {
folderRoot = "storage";
}
if (i > 0) {
if (audioList.get(i - 1).getDATA().contains("emulated")) {
folderRoot1 = "emulated";
} else {
folderRoot1 = "storage";
}
}
if (i == 0) {
ModelAudioFolder model = new ModelAudioFolder("" + folderId + "/" + folderName, "" + folderName, "", "" + folderId + "/" + folderName, "" + count);
folderList.add(model);
Log.d("The_Tag1", "onCreate: " + folderName + " " + folderRoot + " " + folderId + "/" + folderName + " " + count);
} else if (
folderName.equals(new File(audioList.get(i - 1).getDATA()).getParentFile().getName())
&&
folderRoot.equals(folderRoot1)
) {
//exclude
} else {
ModelAudioFolder model = new ModelAudioFolder("" + folderId + "/" + folderName, "" + folderName, "", "" + folderId + "/" + folderName, "" + count);
folderList.add(model);
Log.d("The_Tag1", "onCreate: " + folderName + " " + folderRoot + " " + folderId + "/" + folderName + " " + " " + count);
}
}
return folderList;
}
@Override
protected void onPostExecute(List<ModelAudioFolder> audioFolderList) {
super.onPostExecute(audioFolderList);
Log.d("ModelAudioFolder_Size", "Count:" + audioFolderList.size());
try {
if (audioFolderList.size() <= 0) {
noFoldersRl.setVisibility(View.VISIBLE);
foldersRl.setVisibility(View.GONE);
} else {
noFoldersRl.setVisibility(View.GONE);
foldersRl.setVisibility(View.VISIBLE);
}
Log.d("FoldersSize", "onPostExecute: " + audioFolderList.size());
adapterAudioFolder = new AdapterAudioFolder(getActivity(), audioFolderList, dashboardActivity);
foldersRv.setAdapter(adapterAudioFolder);
} catch (Exception e) {
e.printStackTrace();
}
}
}