Android11正式版已經(jīng)推出,我們將targetSdkVersion和compileSdkVersion都升級(jí)到30输硝,并升級(jí)pixel4到Android11,發(fā)現(xiàn)分區(qū)存儲(chǔ)讀取圖片失敗程梦。經(jīng)過(guò)分析点把,發(fā)現(xiàn)問(wèn)題如下:
context.contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI
, getPhotoProjectionSQL(bucketId)
, getMediaSelectionSQL(true, bucketId)
, null
, getMediaOrderBySQL(start, count + 20))
private fun getMediaOrderBySQL(start: Int = 0, count: Int = Int.MAX_VALUE) = "${MediaStore.MediaColumns.DATE_MODIFIED} DESC limit $count offset $start"
getMediaOrderBySQL()方法拋出異常:
從log上看橘荠,是SQL語(yǔ)句出了問(wèn)題,指向limit郎逃。經(jīng)過(guò)各種嘗試哥童,發(fā)現(xiàn)sortOrder這個(gè)參數(shù)在Android11中只支持排序方式,不再支持limit等查詢限制褒翰。但是通常我們加載相冊(cè)圖片/視頻等是需要分頁(yè)的贮懈,從官方文檔中沒(méi)有找到相應(yīng)的例子說(shuō)明。在閱讀query()方法的源碼优训,我們發(fā)現(xiàn)query()的實(shí)現(xiàn)如下:
public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder,
@Nullable CancellationSignal cancellationSignal) {
Bundle queryArgs = createSqlQueryBundle(selection, selectionArgs, sortOrder);
return query(uri, projection, queryArgs, cancellationSignal);
}
將selection, selectionArgs, sortOrder封裝成一個(gè)Bundle對(duì)象傳給query()方法:
* @param uri The URI, using the content:// scheme, for the content to
* retrieve.
* @param projection A list of which columns to return. Passing null will
* return all columns, which is inefficient.
* @param queryArgs A Bundle containing additional information necessary for
* the operation. Arguments may include SQL style arguments, such
* as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that
* the documentation for each individual provider will indicate
* which arguments they support.
* @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* If the operation is canceled, then {@link OperationCanceledException} will be thrown
* when the query is executed.
* @return A Cursor object, which is positioned before the first entry. May return
* <code>null</code> if the underlying content provider returns <code>null</code>,
* or if it crashes.
* @see Cursor
*/
@Override
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable CancellationSignal cancellationSignal)
不看這段代碼具體的實(shí)現(xiàn)朵你,主要是看注釋對(duì)于queryArgs這個(gè)參數(shù)的說(shuō)明:
* @param queryArgs A Bundle containing additional information necessary for
* the operation. Arguments may include SQL style arguments, such
* as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that
* the documentation for each individual provider will indicate
* which arguments they support.
such as這句:such as {@link ContentResolver#QUERY_ARG_SQL_LIMIT},猜測(cè)QUERY_ARG_SQL_LIMIT可能跟我們需要的limit有關(guān)揣非。往回看Bundle的創(chuàng)建過(guò)程:
Bundle queryArgs = createSqlQueryBundle(selection, selectionArgs, sortOrder);
public static @Nullable Bundle createSqlQueryBundle(
@Nullable String selection,
@Nullable String[] selectionArgs,
@Nullable String sortOrder) {
if (selection == null && selectionArgs == null && sortOrder == null) {
return null;
}
Bundle queryArgs = new Bundle();
if (selection != null) {
queryArgs.putString(QUERY_ARG_SQL_SELECTION, selection);
}
if (selectionArgs != null) {
queryArgs.putStringArray(QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs);
}
if (sortOrder != null) {
queryArgs.putString(QUERY_ARG_SQL_SORT_ORDER, sortOrder);
}
return queryArgs;
}
QUERY_ARG_SQL_SELECTION抡医,QUERY_ARG_SQL_SELECTION_ARGS,QUERY_ARG_SQL_SORT_ORDER都是我們的傳參早敬。猜測(cè)是否可以為queryArgs增加QUERY_ARG_SQL_LIMIT來(lái)實(shí)現(xiàn)limit呢忌傻?于是把createSqlQueryBundle()方法copy到我們的代碼中,并增加QUERY_ARG_SQL_LIMIT搁嗓,如下:
val bundle = createSqlQueryBundle(getMediaSelectionSQL(true, bucketId)
, null
, getMediaOrderBySQLNoLimit(), count + 20, start)
context.contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI
, getPhotoProjectionSQL(bucketId)
, bundle
, null)
private fun createSqlQueryBundle(
selection: String?,
selectionArgs: Array<String?>?,
sortOrder: String?, limitCount: Int = 0, offset: Int = 0): Bundle? {
if (selection == null && selectionArgs == null && sortOrder == null) {
return null
}
val queryArgs = Bundle()
if (selection != null) {
queryArgs.putString(ContentResolver.QUERY_ARG_SQL_SELECTION, selection)
}
if (selectionArgs != null) {
queryArgs.putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs)
}
if (sortOrder != null) {
queryArgs.putString(ContentResolver.QUERY_ARG_SQL_SORT_ORDER, sortOrder)
}
queryArgs.putString(ContentResolver.QUERY_ARG_SQL_LIMIT, "$limitCount offset $offset")
return queryArgs
}
調(diào)用如下:
val bundle = createSqlQueryBundle(getMediaSelectionSQL(false, bucketId)
, null
, getMediaOrderBySQLNoLimit(), count + 20, start)
context.contentResolver.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI
, getVideoProjectionSQL(bucketId)
, bundle
, null)
經(jīng)測(cè)試分頁(yè)加載成功芯勘。