通过Uri查询数据得到Cursor

本文详细介绍了如何通过Uri对数据进行查询,并返回Cursor对象。重点分析了两种方法:Activity.managedQuery()与getContentResolver.query(),并阐述了Activity如何接管Cursor生命周期。提供相关链接供进一步研究。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、通过Uri可以对数据进行查询,返回一个包含特定数据(结果集)的Cursor对象(Cursor提供了遍历查询结果的方法),主要有两种方式:

1.1 Activity.managedQuery()方法(目前被废弃)

根据指定的URI路径信息返回包含特定数据的Cursor对象,应用这个方法可以使Activity接管返回数据对象的生命周期。
参数分析:
URI:  Content Provider 需要返回的资源索引
Projection: 用于标识有哪些columns需要包含在返回数据中。
Selection: 作为查询符合条件的过滤参数,类似于SQL语句中Where之后的条件判断。
SelectionArgs: 同上。
SortOrder: 用于对返回信息进行排序


1.2 getContentResolver.query()

参数和managedQuery相同。





参考资料:http://blog.sina.com.cn/s/blog_5f436e6501012rdw.html

http://blog.youkuaiyun.com/quaful/article/details/6976768(有关Activity接管Cursor生命周期的讨论)

http://blog.youkuaiyun.com/lccly/article/details/6893943


<think>我们使用MediaStore查询图片和视频,并按间(这里指拍摄间或修改间)分排序。 步骤: 1. 分别查询图片和视频,使用MediaStore的日期字段(如DATE_TAKEN、DATE_MODIFIED)进行排序。 2. 将查询结果合并到一个列表中,然后按间排序(或者分别查询并分别排序,然后合并)。 3. 注意:图片和视频的MediaStore Uri不同,且日期字段可能不同。 具体实现: 对于图片,我们可以使用: - Uri: MediaStore.Images.Media.EXTERNAL_CONTENT_URI - 日期字段:MediaStore.Images.Media.DATE_TAKEN(拍摄间)或 MediaStore.Images.Media.DATE_MODIFIED(修改间) 对于视频,使用: - Uri: MediaStore.Video.Media.EXTERNAL_CONTENT_URI - 日期字段:MediaStore.Video.Media.DATE_TAKEN 或 MediaStore.Video.Media.DATE_MODIFIED 我们可以分别查询图片和视频,然后按间排序(降序或升序),最后合并两个Cursor数据?但注意:两个Cursor合并需要自己处理。 另一种方法是分别查询,然后分别得到两个列表,合并后按间排序。但这样可能数据量大会有性能问题。 或者,我们可以使用一个统一的查询,但图片和视频的MediaStore是分开的,所以无法一次查询获取图片和视频。因此,我们只能分别查询,然后合并。 这里我们选择分别查询,然后合并到一个List,再按间排序。 步骤: 1. 查询图片,按日期排序(例如降序,最新在前),同记录是图片型。 2. 查询视频,同样按日期排序(降序),记录是视频型。 3. 将两个列表合并,然后按日期重新排序(因为图片和视频的日期可能交叉)。 但是,如果数据量很大,合并排序可能消耗内存。我们可以考虑使用数据库的UNION,但MediaStore不支持直接UNION。因此,我们只能分别查询后合并。 为了减少内存消耗,我们可以分别查询图片和视频(每次查询限定数量,比如各100条),然后合并排序(取前100条?)。这里我们先不考虑分页,假设数据量不大。 具体代码: 定义数据模型: ```java public class MediaItem { private long id; // 用于区分图片和视频,可以用Uri来打开 private String title; private String path; private long dateTaken; // 间戳,用于排序 private String mimeType; // 型,如图片或视频 private long size; // 文件大小 // 构造方法、getter、setter省略 } ``` 查询图片: ```java private List<MediaItem> loadImageItems() { List<MediaItem> imageItems = new ArrayList<>(); String[] projection = new String[]{ MediaStore.Images.Media._ID, MediaStore.Images.Media.DISPLAY_NAME, MediaStore.Images.Media.DATA, MediaStore.Images.Media.DATE_TAKEN, MediaStore.Images.Media.SIZE, MediaStore.Images.Media.MIME_TYPE }; // 按拍摄间降序排序(最新在前) String sortOrder = MediaStore.Images.Media.DATE_TAKEN + " DESC"; try (Cursor cursor = getContentResolver().query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null, null, sortOrder )) { if (cursor != null) { int idColumn = cursor.getColumnIndex(MediaStore.Images.Media._ID); int nameColumn = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME); int pathColumn = cursor.getColumnIndex(MediaStore.Images.Media.DATA); int dateTakenColumn = cursor.getColumnIndex(MediaStore.Images.Media.DATE_TAKEN); int sizeColumn = cursor.getColumnIndex(MediaStore.Images.Media.SIZE); int mimeTypeColumn = cursor.getColumnIndex(MediaStore.Images.Media.MIME_TYPE); while (cursor.moveToNext()) { long id = cursor.getLong(idColumn); String name = cursor.getString(nameColumn); String path = cursor.getString(pathColumn); long dateTaken = cursor.getLong(dateTakenColumn); long size = cursor.getLong(sizeColumn); String mimeType = cursor.getString(mimeTypeColumn); MediaItem item = new MediaItem(); item.setId(id); item.setTitle(name); item.setPath(path); item.setDateTaken(dateTaken); item.setSize(size); item.setMimeType(mimeType); imageItems.add(item); } } } catch (Exception e) { e.printStackTrace(); } return imageItems; } ``` 查询视频(似): ```java private List<MediaItem> loadVideoItems() { List<MediaItem> videoItems = new ArrayList<>(); String[] projection = new String[]{ MediaStore.Video.Media._ID, MediaStore.Video.Media.DISPLAY_NAME, MediaStore.Video.Media.DATA, MediaStore.Video.Media.DATE_TAKEN, MediaStore.Video.Media.SIZE, MediaStore.Video.Media.MIME_TYPE }; String sortOrder = MediaStore.Video.Media.DATE_TAKEN + " DESC"; try (Cursor cursor = getContentResolver().query( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, projection, null, null, sortOrder )) { if (cursor != null) { int idColumn = cursor.getColumnIndex(MediaStore.Video.Media._ID); int nameColumn = cursor.getColumnIndex(MediaStore.Video.Media.DISPLAY_NAME); int pathColumn = cursor.getColumnIndex(MediaStore.Video.Media.DATA); int dateTakenColumn = cursor.getColumnIndex(MediaStore.Video.Media.DATE_TAKEN); int sizeColumn = cursor.getColumnIndex(MediaStore.Video.Media.SIZE); int mimeTypeColumn = cursor.getColumnIndex(MediaStore.Video.Media.MIME_TYPE); while (cursor.moveToNext()) { long id = cursor.getLong(idColumn); String name = cursor.getString(nameColumn); String path = cursor.getString(pathColumn); long dateTaken = cursor.getLong(dateTakenColumn); long size = cursor.getLong(sizeColumn); String mimeType = cursor.getString(mimeTypeColumn); MediaItem item = new MediaItem(); item.setId(id); item.setTitle(name); item.setPath(path); item.setDateTaken(dateTaken); item.setSize(size); item.setMimeType(mimeType); videoItems.add(item); } } } catch (Exception e) { e.printStackTrace(); } return videoItems; } ``` 合并并排序(按间降序): ```java List<MediaItem> allMedia = new ArrayList<>(); allMedia.addAll(loadImageItems()); allMedia.addAll(loadVideoItems()); // 按dateTaken降序排序(最新在前) Collections.sort(allMedia, new Comparator<MediaItem>() { @Override public int compare(MediaItem o1, MediaItem o2) { return Long.compare(o2.getDateTaken(), o1.getDateTaken()); } }); ``` 注意:如果图片或视频没有拍摄间(DATE_TAKEN),可以使用DATE_MODIFIED(修改间)作为备选。上面的代码中,我们只使用了DATE_TAKEN。如果DATE_TAKEN为0,则可能排序不准确。因此,我们可以改进: 在查询图片,如果DATE_TAKEN为0,我们可以使用DATE_MODIFIED(MediaStore.Images.Media.DATE_MODIFIED)乘以1000(因为DATE_MODIFIED是秒,而DATE_TAKEN是毫秒)来替代。 修改投影和取值: 投影中添加DATE_MODIFIED字段: ```java String[] projection = new String[]{ ..., MediaStore.Images.Media.DATE_MODIFIED }; ``` 然后,在读取: ```java int dateModifiedColumn = cursor.getColumnIndex(MediaStore.Images.Media.DATE_MODIFIED); long dateTaken = cursor.getLong(dateTakenColumn); if (dateTaken == 0) { long dateModified = cursor.getLong(dateModifiedColumn) * 1000; // 转为毫秒 dateTaken = dateModified; } ``` 视频同理。 另外,注意权限:需要READ_EXTERNAL_STORAGE权限。 如果要在同一个列表中显示图片和视频,并且按间排序,这种方法可以满足。 如果数据量很大,可以考虑使用分页加载,但MediaStore本身支持分页(通过Cursor的分页),但合并后分页就比较复杂。这里我们不做分页处理。 总结:通过分别查询图片和视频,然后合并列表并排序,我们可以实现按间分排序。 引用说明: - 参考了引用[1]中获取图片路径和拍摄间的方法[^1]。 - 参考了引用[3]中查询视频的方法和排序参数的使用[^3]。 相关问题: 1. 如何按其他字段(如文件名)排序? 2. 如何实现分页加载媒体文件? 3. 如果媒体文件很多,如何优化性能?</think>在 Android 中使用 Cursor 查询图片和视频并按间分排序,可以通过以下步骤实现: ### 1. 查询图片并按间排序 ```java // 图片查询投影列 String[] imageProjection = { MediaStore.Images.Media._ID, MediaStore.Images.Media.DISPLAY_NAME, MediaStore.Images.Media.DATE_TAKEN, // 拍摄间 MediaStore.Images.Media.DATE_MODIFIED, // 修改间 MediaStore.Images.Media.DATA }; // 按拍摄间降序排序(最新在前) String imageSortOrder = MediaStore.Images.Media.DATE_TAKEN + " DESC"; try (Cursor imageCursor = getContentResolver().query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageProjection, null, // 无筛选条件 null, imageSortOrder)) { if (imageCursor != null) { while (imageCursor.moveToNext()) { String path = imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media.DATA)); long dateTaken = imageCursor.getLong(imageCursor.getColumnIndex(MediaStore.Images.Media.DATE_TAKEN)); long dateModified = imageCursor.getLong(imageCursor.getColumnIndex(MediaStore.Images.Media.DATE_MODIFIED)); // 按日期分处理(示例) Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(dateTaken); int year = calendar.get(Calendar.YEAR); int month = calendar.get(Calendar.MONTH); // TODO: 根据年月分存储 } } } ``` ### 2. 查询视频并按间排序 ```java // 视频查询投影列 String[] videoProjection = { MediaStore.Video.Media._ID, MediaStore.Video.Media.DISPLAY_NAME, MediaStore.Video.Media.DATE_TAKEN, // 拍摄间 MediaStore.Video.Media.DATE_MODIFIED, // 修改间 MediaStore.Video.Media.DATA }; // 按修改间降序排序(最新在前) String videoSortOrder = MediaStore.Video.Media.DATE_MODIFIED + " DESC"; try (Cursor videoCursor = getContentResolver().query( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, videoProjection, null, // 无筛选条件 null, videoSortOrder)) { if (videoCursor != null) { while (videoCursor.moveToNext()) { String path = videoCursor.getString(videoCursor.getColumnIndex(MediaStore.Video.Media.DATA)); long dateTaken = videoCursor.getLong(videoCursor.getColumnIndex(MediaStore.Video.Media.DATE_TAKEN)); long dateModified = videoCursor.getLong(videoCursor.getColumnIndex(MediaStore.Video.Media.DATE_MODIFIED)); // TODO: 根据日期分处理 } } } ``` ### 3. 合并查询并按间分 ```java // 定义媒体项数据结构 class MediaItem { String path; long date; boolean isVideo; } List<MediaItem> allMedia = new ArrayList<>(); // 查询图片 try (Cursor cursor = getContentResolver().query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[]{MediaStore.Images.Media.DATA, MediaStore.Images.Media.DATE_TAKEN}, null, null, null)) { while (cursor != null && cursor.moveToNext()) { MediaItem item = new MediaItem(); item.path = cursor.getString(0); item.date = cursor.getLong(1); item.isVideo = false; allMedia.add(item); } } // 查询视频 try (Cursor cursor = getContentResolver().query( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, new String[]{MediaStore.Video.Media.DATA, MediaStore.Video.Media.DATE_TAKEN}, null, null, null)) { while (cursor != null && cursor.moveToNext()) { MediaItem item = new MediaItem(); item.path = cursor.getString(0); item.date = cursor.getLong(1); item.isVideo = true; allMedia.add(item); } } // 按日期降序排序 Collections.sort(allMedia, (o1, o2) -> Long.compare(o2.date, o1.date)); // 按年月分 Map<String, List<MediaItem>> classifiedMedia = new HashMap<>(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM", Locale.getDefault()); for (MediaItem item : allMedia) { String monthKey = sdf.format(new Date(item.date)); if (!classifiedMedia.containsKey(monthKey)) { classifiedMedia.put(monthKey, new ArrayList<>()); } classifiedMedia.get(monthKey).add(item); } ``` ### 关键点说明: 1. **字段选择**: - `DATE_TAKEN`:拍摄间(优先使用) - `DATE_MODIFIED`:最后修改间(备选) - 间单位为毫秒(UNIX 间戳) 2. **排序方式**: - `ASC`:升序(旧→新) - `DESC`:降序(新→旧) - 示例:`MediaStore.Images.Media.DATE_TAKEN + " DESC"` 3. **分方法**: - 使用 `Calendar` 或 `SimpleDateFormat` 提取年月 - 使用 `Map<String, List>` 存储分结果 - 键格式如 "2023-08" 表示 2023年8月 4. **注意事项**: - 需要 `READ_EXTERNAL_STORAGE` 权限 - Android 10+ 需添加 `requestLegacyExternalStorage` - 使用 `try-with-resources` 确保 Cursor 关闭 ### 性能优化建议: 1. **分页查询**:添加 `LIMIT` 和 `OFFSET` 参数 ```java String limit = "20"; // 每页数量 String offset = String.valueOf(pageIndex * 20); // 偏移量 ``` 2. **异步加载**:在后台线程执行查询 ```java new Thread(() -> { // 执行查询操作 runOnUiThread(() -> { // 更新UI }); }).start(); ``` 3. **筛选条件**:添加日期范围过滤 ```java String selection = MediaStore.Images.Media.DATE_TAKEN + " BETWEEN ? AND ?"; String[] selectionArgs = { String.valueOf(startTimestamp), String.valueOf(endTimestamp) }; ``` 4. **使用内容观察者**:监听媒体库更新 ```java getContentResolver().registerContentObserver( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { // 重新加载数据 } }); ``` ### 引用说明: - 使用 `DATE_TAKEN` 字段获取媒体间[^1] - 通过 `Cursor` 查询数据库并处理结果[^2] - 使用 `sortOrder` 参数实现排序[^3][^4] - 结合日期函数进行分处理[^1][^4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值