Android Q 设备上query查询图片资源crash
设备:Android Q模拟器
compileSdkVersion: 29
2019-08-15 17:10:37.338 W/System.err: java.lang.RuntimeException: An error occurred while executing doInBackground()
2019-08-15 17:10:37.338 W/System.err: at androidx.loader.content.ModernAsyncTask$3.done(ModernAsyncTask.java:164)
2019-08-15 17:10:37.338 W/System.err: at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
2019-08-15 17:10:37.338 W/System.err: at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
2019-08-15 17:10:37.338 W/System.err: at java.util.concurrent.FutureTask.run(FutureTask.java:271)
2019-08-15 17:10:37.338 W/System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
2019-08-15 17:10:37.338 W/System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
2019-08-15 17:10:37.338 W/System.err: at java.lang.Thread.run(Thread.java:919)
2019-08-15 17:10:37.339 W/System.err: Caused by: java.lang.IllegalArgumentException: Invalid column COUNT(*) AS count
2019-08-15 17:10:37.339 W/System.err: at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:170)
2019-08-15 17:10:37.347 W/System.err: at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:140)
2019-08-15 17:10:37.347 W/System.err: at android.content.ContentProviderProxy.query(ContentProviderNative.java:423)
2019-08-15 17:10:37.352 W/System.err: at android.content.ContentResolver.query(ContentResolver.java:944)
2019-08-15 17:10:37.355 W/System.err: at android.content.ContentResolver.query(ContentResolver.java:880)
2019-08-15 17:10:37.355 W/System.err: at androidx.core.content.ContentResolverCompat.query(ContentResolverCompat.java:81)
2019-08-15 17:10:37.355 W/System.err: at androidx.loader.content.CursorLoader.loadInBackground(CursorLoader.java:63)
2019-08-15 17:10:37.355 W/System.err: at com.zhihu.matisse.internal.loader.AlbumLoader.loadInBackground(AlbumLoader.java:98)
2019-08-15 17:10:37.356 W/System.err: at com.zhihu.matisse.internal.loader.AlbumLoader.loadInBackground(AlbumLoader.java:34)
2019-08-15 17:10:37.383 W/System.err: at androidx.loader.content.AsyncTaskLoader.onLoadInBackground(AsyncTaskLoader.java:307)
2019-08-15 17:10:37.384 W/System.err: at androidx.loader.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:60)
2019-08-15 17:10:37.384 W/System.err: at androidx.loader.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:48)
2019-08-15 17:10:37.384 W/System.err: at androidx.loader.content.ModernAsyncTask$2.call(ModernAsyncTask.java:141)
2019-08-15 17:10:37.384 W/System.err: at java.util.concurrent.FutureTask.run(FutureTask.java:266)
AlbumLoader类做了Android Q的处理,代码如下:
package com.zhihu.matisse.internal.loader;
import android.content.Context;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.MergeCursor;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import android.util.SparseArray;
import androidx.loader.content.CursorLoader;
import com.zhihu.matisse.internal.entity.Album;
import com.zhihu.matisse.internal.entity.SelectionSpec;
/**
private static final String SELECTION =
"(" + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
+ " OR "
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "=?)"
+ " AND " + MediaStore.MediaColumns.SIZE + ">0"
+ ") GROUP BY (bucket_id";
private static final String SELECTION_29 =
"(" + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
+ " OR "
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "=?)"
+ " AND " + MediaStore.MediaColumns.SIZE + ">0";
private static final String[] SELECTION_ARGS = {
String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE),
String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO),
};
private static final String SELECTION_FOR_SINGLE_MEDIA_TYPE =
MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
+ " AND " + MediaStore.MediaColumns.SIZE + ">0"
+ ") GROUP BY (bucket_id";
private static final String SELECTION_FOR_SINGLE_MEDIA_TYPE_29 =
MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
+ " AND " + MediaStore.MediaColumns.SIZE + ">0";
private static String[] getSelectionArgsForSingleMediaType(int mediaType) {
return new String[]{String.valueOf(mediaType)};
}
private static final String BUCKET_ORDER_BY = "datetaken DESC";
private AlbumLoader(Context context, String[] projection, String selection, String[] selectionArgs) {
super(context, QUERY_URI, projection, selection, selectionArgs, BUCKET_ORDER_BY);
}
public static CursorLoader newInstance(Context context) {
String selection;
String[] selectionArgs;
String[] projection;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
projection = PROJECTION_29;
if (SelectionSpec.getInstance().onlyShowImages()) {
selection = SELECTION_FOR_SINGLE_MEDIA_TYPE_29;
selectionArgs = getSelectionArgsForSingleMediaType(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE);
} else if (SelectionSpec.getInstance().onlyShowVideos()) {
selection = SELECTION_FOR_SINGLE_MEDIA_TYPE_29;
selectionArgs = getSelectionArgsForSingleMediaType(MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO);
} else {
selection = SELECTION_29;
selectionArgs = SELECTION_ARGS;
}
} else {
projection = PROJECTION;
if (SelectionSpec.getInstance().onlyShowImages()) {
selection = SELECTION_FOR_SINGLE_MEDIA_TYPE;
selectionArgs = getSelectionArgsForSingleMediaType(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE);
} else if (SelectionSpec.getInstance().onlyShowVideos()) {
selection = SELECTION_FOR_SINGLE_MEDIA_TYPE;
selectionArgs = getSelectionArgsForSingleMediaType(MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO);
} else {
selection = SELECTION;
selectionArgs = SELECTION_ARGS;
}
}
return new AlbumLoader(context, projection, selection, selectionArgs);
}
@Override
public Cursor loadInBackground() {
Cursor albums = super.loadInBackground();
MatrixCursor allAlbum = new MatrixCursor(COLUMNS);
int totalCount = 0;
String allAlbumCoverPath = "";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (albums == null) {
return allAlbum;
}
SparseArray<Album> albumList = new SparseArray<>();
while (albums.moveToNext()) {
int bucketId = albums.getInt(albums.getColumnIndex(COLUMN_BUCKET_ID));
String bucketName = albums.getString(albums.getColumnIndex(COLUMN_BUCKET_NAME));
Album album = albumList.get(bucketId);
if (album == null) {
album = new Album(String.valueOf(bucketId), allAlbumCoverPath, bucketName, 0);
albumList.append(bucketId, album);
}
album.addCaptureCount();
}
for (int i = 0; i < albumList.size(); i++) {
totalCount += albumList.valueAt(i).getCount();
}
if (albums.moveToFirst()) {
allAlbumCoverPath = albums.getString(albums.getColumnIndex(MediaStore.MediaColumns.DATA));
allAlbum.addRow(new String[]{Album.ALBUM_ID_ALL, Album.ALBUM_ID_ALL, Album.ALBUM_NAME_ALL, allAlbumCoverPath,
String.valueOf(totalCount)});
}
for (int i = 0; i < albumList.size(); i++) {
Album album = albumList.valueAt(i);
totalCount += album.getCount();
allAlbum.addRow(new String[]{album.getId(), album.getId(), album.getDisplayName(getContext()),
album.getCoverPath(), String.valueOf(album.getCount())});
}
return allAlbum;
} else {
if (albums != null) {
while (albums.moveToNext()) {
totalCount += albums.getInt(albums.getColumnIndex(COLUMN_COUNT));
}
if (albums.moveToFirst()) {
allAlbumCoverPath = albums.getString(albums.getColumnIndex(MediaStore.MediaColumns.DATA));
}
}
allAlbum.addRow(new String[]{Album.ALBUM_ID_ALL, Album.ALBUM_ID_ALL, Album.ALBUM_NAME_ALL, allAlbumCoverPath,
String.valueOf(totalCount)});
return new MergeCursor(new Cursor[]{allAlbum, albums});
}
}
@Override
public void onContentChanged() {
// FIXME a dirty way to fix loading multiple times
}
}
encounter the same situation. looking for solution
当使用兼容的AlbumLoader 之后,如果视频库没有文件,会出现以下错误:
Process: com.zhihu.matisse.sample, PID: 10492
android.database.CursorIndexOutOfBoundsException: After last row.
at android.database.MatrixCursor.get(MatrixCursor.java:79)
at android.database.MatrixCursor.getString(MatrixCursor.java:266)
at com.zhihu.matisse.internal.entity.Album.valueOf(Album.java:70)
at com.zhihu.matisse.internal.ui.widget.AlbumsSpinner.onItemSelected(AlbumsSpinner.java:75)
at com.zhihu.matisse.internal.ui.widget.AlbumsSpinner.setSelection(AlbumsSpinner.java:68)
at com.zhihu.matisse.ui.MatisseActivity$1.run(MatisseActivity.java:364)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
On Android Q,the projection parameter of ContentResolver.query() no longer support custom column like xxx AS xx,instead, it throws IllegalArgumentException because of the setting in MediaProvider.java : qb.setStrict(true).
SQLiteBuilder#computeProjection():
if (!mStrict &&
( userColumn.contains(" AS ") || userColumn.contains(" as "))) {
/* A column alias already exist */
projection[i] = maybeWithOperator(operator, userColumn);
continue;
}
// If greylist is configured, we might be willing to let
// this custom column bypass our strict checks.
if (mProjectionGreylist != null) {
...
}
throw new IllegalArgumentException("Invalid column "
+ projectionIn[i]);
In MediaProvider.java:
MediaProvider#getQueryBuilderInternal():
final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
if (parseBoolean(uri.getQueryParameter("distinct"))) {
qb.setDistinct(true);
}
qb.setProjectionAggregationAllowed(true);
qb.setStrict(true);//Only on Android Q
Most helpful comment
encounter the same situation. looking for solution