有没有办法限制游标返回的行数? 我的手机有大约 4000 个联系人,我只需要其中一些。
这是我正在使用的代码
db = new dBHelper(this);
ContentResolver cr = getContentResolver();
Cursor cursor;
cursor = cr.query(ContactsContract.Contacts.CONTENT_URI,null, null, null, ContactName + " ASC");
Log.i(TAG, CLASSNAME + " got contacts entries");
for (int it = 0; it <100 ; it++){//cursor.getCount()
Log.i(TAG, CLASSNAME + " getting string");
String mytimes_contacted = cursor.getString(cursor.getColumnIndex(dBHelper.times_contacted));
Log.i(TAG, CLASSNAME + " done from the string");
}
我得到的日志是
I/Check(11506): [ContactsPicker] got contacts entries
I/Check(11506): [ContactsPicker] getting first string
D/AndroidRuntime(11506): Shutting down VM
W/dalvikvm(11506): threadid=1: thread exiting with uncaught exception (group=0x2aac8578)
D/dalvikvm(11541): GC_CONCURRENT freed 923K, 46% free 4000K/7303K, external 1685K/2133K, paused 1ms+8ms
E/AndroidRuntime(11506): FATAL EXCEPTION: main
E/AndroidRuntime(11506): java.lang.RuntimeException: Unable to start activity ComponentInfo{~~my package name~~}: android.database.CursorIndexOutOfBoundsException: Index -1 requested, with a size of 3537
要限制光标中的结果数量,请尝试:
cursor = cr.query(ContactsContract.Contacts.CONTENT_URI,null, null, null, ContactName + " LIMIT 100");
while(cursor.moveToNext()) {
// something clever
}
从Android 11开始,上述解决方案将不起作用,您可以尝试这个来获取数据。
/**
* Call to fetch all media on device, it but be called synchronously since function is called on a background thread
*/
private fun fetchGalleryImages(
context: Context,
offset: Int,
limit: Int
): List<MediaItem> {
val galleryImageUrls = mutableListOf<MediaItem>()
try {
if (EasyPermissions.hasPermissions(
context,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
) {
// Define the columns that will be fetched
val projection = arrayOf(
MediaStore.Files.FileColumns._ID,
MediaStore.Files.FileColumns.DATA,
MediaStore.Files.FileColumns.DATE_ADDED,
MediaStore.Files.FileColumns.MEDIA_TYPE,
MediaStore.Files.FileColumns.MIME_TYPE,
MediaStore.Files.FileColumns.TITLE,
MediaStore.Video.Media.DURATION
)
val selection =
"${MediaStore.Files.FileColumns.MEDIA_TYPE} = ? OR ${MediaStore.Files.FileColumns.MEDIA_TYPE} = ?"
val selectionArgs = arrayOf(
MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE.toString(),
MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO.toString()
)
/**
* Change the way to fetch Media Store
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// Get All data in Cursor by sorting in DESC order
context.contentResolver.query(
contentUri(),
projection,
Bundle().apply {
// Limit & Offset
putInt(ContentResolver.QUERY_ARG_LIMIT, limit)
putInt(ContentResolver.QUERY_ARG_OFFSET, offset)
// Sort function
putStringArray( // <-- This should be an array. I spent a whole day trying to figure out what I was doing wrong
ContentResolver.QUERY_ARG_SORT_COLUMNS,
arrayOf(MediaStore.Files.FileColumns.DATE_MODIFIED)
)
putInt(
ContentResolver.QUERY_ARG_SORT_DIRECTION,
ContentResolver.QUERY_SORT_DIRECTION_DESCENDING
)
// Selection
putString(ContentResolver.QUERY_ARG_SQL_SELECTION, selection)
putStringArray(
ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS,
selectionArgs
)
}, null
)
} else {
val sortOrder =
"${MediaStore.Files.FileColumns.DATE_MODIFIED} DESC LIMIT $limit OFFSET $offset"
// Get All data in Cursor by sorting in DESC order
context.contentResolver.query(
contentUri(),
projection,
selection,
selectionArgs,
sortOrder
)
}?.use { cursor ->
while (cursor.moveToNext()) {
galleryImageUrls.add(
MediaItem(
cursor.getLong(cursor.getColumnIndex(MediaStore.Files.FileColumns._ID)),
ContentUris.withAppendedId(
contentUri(),
cursor.getLong(cursor.getColumnIndex(MediaStore.Files.FileColumns._ID))
),
cursor.getString(cursor.getColumnIndex(MediaStore.Files.FileColumns.DATA)),
cursor.getStringOrNull(cursor.getColumnIndex(MediaStore.Files.FileColumns.MIME_TYPE)),
cursor.getLongOrNull(cursor.getColumnIndex(MediaStore.Video.Media.DURATION))
)
)
}
}
}
} catch (ex: Exception) {
ex.printStackTrace()
}
return galleryImageUrls
}
接受的答案对于 android 11 不再有效。在 android 11 中添加了一个约束,不允许在排序值中使用 LIMIT。您需要使用带有捆绑参数的查询。例如:
val bundle = Bundle().apply {
putInt(ContentResolver.QUERY_ARG_LIMIT, 100)
}
resolver.query(
ContactsContract.Contacts.CONTENT_URI,
projection,
bundle,
null
)
在android 26中查询方法进行了升级。该函数正在使用这些参数。 Uri uri、String[] 投影、Bundle queryArgs、CancellationSignal CancellationSignal
下面的示例我获取了最近的 5 张照片。
val whereArgs = arrayOf("image/jpeg", "image/png", "image/jpg")
val projection = arrayOf(MediaStore.Images.ImageColumns._ID,
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns.MIME_TYPE)
val selection =
"${MediaStore.Files.FileColumns.MIME_TYPE} = ? OR ${MediaStore.Files.FileColumns.MIME_TYPE} = ? OR ${MediaStore.Files.FileColumns.MIME_TYPE} = ?"
val queryArgs = Bundle()
val sortArgs = arrayOf(MediaStore.Images.ImageColumns.DATE_TAKEN)
queryArgs.putStringArray(ContentResolver.QUERY_ARG_SORT_COLUMNS, sortArgs)
queryArgs.putInt(ContentResolver.QUERY_ARG_SORT_DIRECTION, ContentResolver.QUERY_SORT_DIRECTION_DESCENDING)
queryArgs.putInt(ContentResolver.QUERY_ARG_LIMIT, 5)
queryArgs.putString(ContentResolver.QUERY_ARG_SQL_SELECTION, selection)
queryArgs.putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS, whereArgs)
val cursor = context!!.contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection,
queryArgs,
null)
if (cursor!!.moveToFirst()) {
do {
val imageLocation = cursor.getString(1)
val imageFile = File(imageLocation)
if (imageFile.exists()) {
//access you file from imageLocation
}
} while (cursor.moveToNext())
fiveRecentlyImagesAdapter!!.notifyDataSetChanged()
}
如果有人正在寻找上述 Ignacio Tomas Crespo 答案的 Java 版本,
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
cursor = context.getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
.buildUpon()
.encodedQuery("limit=" + offSet + "," + "100")
.build(),
columns,
null,
null,
null);
} else {
Bundle bundle = new Bundle();
bundle.putInt(ContentResolver.QUERY_ARG_LIMIT, 100);
cursor = context.getContentResolver()
.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
columns,
bundle,
null);
}
我的解决方案更像是手动的。我刚刚传递了可选的 limit 参数并修改了 while 循环条件,它对我有用。无需担心Android版本。在我的应用程序中,我加载前 20 个曲目,然后加载所有曲目,因此用户无需等待整个列表被获取。
您可以对图像、视频等应用同样的方法。
suspend fun getTracksFromMusicFolder(limit: Int? = null): ArrayList<Track> {
return withContext(Dispatchers.IO) {
var limitVal = 0
val result = arrayListOf<Track>()
val contentResolver = appContext.get()?.contentResolver
val projection = arrayOf(
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Media.MIME_TYPE,
MediaStore.Audio.Media.DATE_MODIFIED,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.DURATION
)
val sortOrder = "${MediaStore.Audio.Media.TITLE} ASC"
val selection = MediaStore.Audio.Media.IS_MUSIC + " != 0"
contentResolver?.query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
projection,
selection,
null,
sortOrder
).use {
it?.let { cursor ->
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID)
val titleColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE)
val artistColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST)
val albumColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM)
val durationColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION)
val dateModifiedColumn =
cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATE_MODIFIED)
while (cursor.moveToNext() && limitVal != limit) {
val id = cursor.getLong(idColumn)
val title = cursor.getString(titleColumn).trimStart()
val artist = cursor.getString(artistColumn).trimStart()
val album = cursor.getString(albumColumn).trimStart()
val duration = cursor.getLong(durationColumn)
val dateModified = cursor.getLong(dateModifiedColumn)
val path =
ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id)
val pathExtension = getFileMimeType(path)
val track = Track(
id,
title,
artist,
album,
path,
duration,
pathExtension,
dateAdded = dateModified
)
result.add(track)
limitVal += 1
}
}
}
result
}
}
用法示例
val firstTwentyTrack = getTracksFromMusicFolder(20)
val allTracks = getTracksFromMusicFolder(null)