Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android Q 设备上query查询图片资源crash #646

Open
Jocerly opened this issue Aug 15, 2019 · 4 comments
Open

Android Q 设备上query查询图片资源crash #646

Jocerly opened this issue Aug 15, 2019 · 4 comments

Comments

@Jocerly
Copy link

Jocerly commented Aug 15, 2019

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)

@Jocerly
Copy link
Author

Jocerly commented Aug 16, 2019

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;

/**

  • Load all albums (grouped by bucket_id) into a single cursor.
    /
    public class AlbumLoader extends CursorLoader {
    public static final String COLUMN_COUNT = "count";
    private static final Uri QUERY_URI = MediaStore.Files.getContentUri("external");
    private static final String COLUMN_BUCKET_ID = "bucket_id";
    private static final String COLUMN_BUCKET_NAME = "bucket_display_name";
    private static final String[] COLUMNS = {
    MediaStore.Files.FileColumns._ID,
    COLUMN_BUCKET_ID,
    COLUMN_BUCKET_NAME,
    MediaStore.MediaColumns.DATA,
    COLUMN_COUNT};
    private static final String[] PROJECTION = {
    MediaStore.Files.FileColumns._ID,
    COLUMN_BUCKET_ID,
    COLUMN_BUCKET_NAME,
    MediaStore.MediaColumns.DATA,
    "COUNT(
    ) AS " + COLUMN_COUNT};
    private static final String[] PROJECTION_29 = {
    MediaStore.Files.FileColumns._ID,
    COLUMN_BUCKET_ID,
    COLUMN_BUCKET_NAME,
    MediaStore.MediaColumns.DATA};

    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 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
    }
    }

@2quarius
Copy link

encounter the same situation. looking for solution

@zhanyage
Copy link

当使用兼容的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)

moodcal pushed a commit to moodcal/Matisse that referenced this issue Mar 7, 2020
@wade183
Copy link

wade183 commented May 12, 2020

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants