Android ContentResolver地点增删改查详解

Android 中 ContentResolver 的增删改查详解

ContentResolver 是 Android 提供的 API,用于在不同应用程序之间访问和操作数据,通常与 ContentProvider 一起使用。它提供 增(insert)、删(delete)、改(update)、查(query) 操作,常见于访问系统数据库(如通讯录、媒体库等)或应用自定义的数据库(如日记本、笔记等)。


📌 使用 ContentResolver 的步骤

  1. 确定 ContentProvider 提供的数据 URI (content:// 格式)
  2. 获取 ContentResolver 对象
  3. 执行增、删、改、查操作
  4. 处理权限(如果是访问系统数据)
  5. 关闭 Cursor 以释放资源(仅 query 需要)

💡 1. 获取 ContentResolver

在 Activity 或 Fragment 中:

ini 复制代码
val contentResolver = context.contentResolver

在 Service 中:

ini 复制代码
val contentResolver = applicationContext.contentResolver

🔍 2. 查询数据(query)

查询 ContentProvider 提供的数据,返回 Cursor,需手动遍历和关闭。

示例:查询日记

kotlin 复制代码
// 1. 目标 ContentProvider 的 URI
val uri = Uri.parse("content://com.example.provider/diary")
//构造 content:// 开头的 Uri,它用于标识数据库中的某条数据。
​
// 2. 查询的字段(列)
val projection = arrayOf("id", "title", "date")
​
// 3. 查询条件(WHERE)
val selection = "title LIKE ?"
val selectionArgs = arrayOf("%心情%") // 查询包含"心情"的日记
//这表示查询 title 列,要求 title 的内容包含"心情"两个字。
//LIKE 是 SQL 语法,用于进行模糊查询。
//? 是占位符,用来防止 SQL 注入,具体值由 selectionArgs 提供。
​
​
​
// 4. 排序方式
val sortOrder = "date DESC"
​
// 5. 执行查询
val cursor = contentResolver.query(uri, projection, selection, selectionArgs, sortOrder)
​
cursor?.use { 
    while (it.moveToNext()) {
        val id = it.getInt(it.getColumnIndexOrThrow("id"))
        val title = it.getString(it.getColumnIndexOrThrow("title"))
        val date = it.getLong(it.getColumnIndexOrThrow("date"))
        println("ID: $id, Title: $title, Date: $date")
    }
} // `use` 语法确保 Cursor 关闭

🔹 注意事项:

  • 查询时使用 LIKE 支持模糊匹配,? 代表参数占位符
  • 使用 Cursor 需要调用 .moveToNext() 逐行读取
  • 关闭 Cursor 以释放资源

📝 3. 插入数据(insert)

用于新增数据,返回新数据的 Uri。

示例:新增日记

scss 复制代码
val uri = Uri.parse("content://com.example.provider/diary")
​
// 1. 创建 ContentValues,存储字段和值
val values = ContentValues().apply {
    put("title", "今日心情")
    put("content", "今天是个好天气")
    put("date", System.currentTimeMillis())
}
​
// 2. 调用 insert 方法
val newUri = contentResolver.insert(uri, values)
​
// 3. 判断插入结果
if (newUri != null) {
    println("插入成功,URI: $newUri")
} else {
    println("插入失败")
}

🔹 注意事项:

  • ContentValues 类似 Map,用于存储字段和值
  • insert() 返回新数据的 Uri,失败时返回 null
  • newUri:表示新插入的数据的 URI ,用于后续操作。如果插入失败,返回 null。

✏ 4. 更新数据(update)

用于修改已有数据,返回影响的行数。

示例:更新日记内容

kotlin 复制代码
val uri = Uri.parse("content://com.example.provider/diary")
​
// 1. 创建 ContentValues,存储更新的字段和值
val values = ContentValues().apply {
    put("content", "更新后的日记内容")
}
​
// 2. 更新条件
val selection = "id = ?"
val selectionArgs = arrayOf("1") // 只更新 ID 为 1 的日记
​
// 3. 执行 update
val rowsUpdated = contentResolver.update(uri, values, selection, selectionArgs)
​
println("更新了 $rowsUpdated 条数据")

🔹 注意事项:

  • selection 是 SQL 语法的 WHERE 子句
  • update() 返回更新的行数,可能为 0(表示数据未找到)

🗑 5. 删除数据(delete)

用于删除数据,返回删除的行数。

示例:删除指定日记

kotlin 复制代码
val uri = Uri.parse("content://com.example.provider/diary")
​
// 1. 删除条件
val selection = "id = ?"
val selectionArgs = arrayOf("1") // 只删除 ID 为 1 的日记
​
// 2. 执行删除
val rowsDeleted = contentResolver.delete(uri, selection, selectionArgs)
​
println("删除了 $rowsDeleted 条数据")

🔹 注意事项:

  • delete() 返回删除的行数
  • 不提供 selection 则删除所有数据(谨慎)

📖 6. 访问系统 ContentProvider 示例

📌 读取联系人

kotlin 复制代码
val uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI
val cursor = contentResolver.query(uri, null, null, null, null)
​
cursor?.use {
    while (it.moveToNext()) {
        val name = it.getString(it.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))
        val phone = it.getString(it.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER))
        println("联系人: $name, 电话: $phone")
    }
}

📌 读取相册

kotlin 复制代码
val uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(MediaStore.Images.Media.DATA)
val cursor = contentResolver.query(uri, projection, null, null, null)
​
cursor?.use {
    while (it.moveToNext()) {
        val imagePath = it.getString(it.getColumnIndexOrThrow(MediaStore.Images.Media.DATA))
        println("图片路径: $imagePath")
    }
}

🔒 7. 处理权限

如果访问系统 ContentProvider(如通讯录、相册等),需申请权限:

在 AndroidManifest.xml 申请权限

ini 复制代码
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

动态请求权限(Android 6.0+)

kotlin 复制代码
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_CONTACTS), 1)
}

📌 总结

操作 方法名 主要参数
查询 query(uri, projection, selection, selectionArgs, sortOrder) Uri,字段数组,查询条件,排序
插入 insert(uri, values) Uri,ContentValues
更新 update(uri, values, selection, selectionArgs) Uri,ContentValues,条件
删除 delete(uri, selection, selectionArgs) Uri,条件

💡 你是要操作自己的 ContentProvider 还是访问系统数据?如果是日记本的 ContentProvider,可以一起设计 URI 和数据库结构。

相关推荐
高频因子挖掘机2 小时前
回测收益为什么可能不真实?从时间戳、数据可用性到未来函数检查
后端·github·api
Java后端的Ai之路10 小时前
06-React-Props
前端·react.js·前端框架
行百里er10 小时前
Redis 完结篇——Redis 热点问题在 Spring Boot 项目中实战
redis·后端
Flynt10 小时前
开源"间谍卫星"说数据全是真的,我把它的免key数据源挨个试了一遍
前端·开源·数据可视化
Csvn11 小时前
渲染管线
前端
程序员大阳11 小时前
副队长CSS教程(33)–圆角与透明
前端·css·透明·圆角
高频因子挖掘机11 小时前
只取最近 60 个交易日,却每次拉全历史:量化脚本变慢的原因与改法
后端·github·api
志尊宝11 小时前
Vue3 零基础每日笔记(082):文件上传实战——el-upload 校验、自定义上传、头像预览
前端·javascript·vue.js·笔记·#javascript·#vue.js
鬼手点金11 小时前
opencode-rules 插件
java·开发语言·前端·人工智能·chrome·git·github
柳杉12 小时前
借助 Codex 和可视化工作流,从调研到完整实现,3D 大屏竟然可以这么简单
前端·ai编程·数据可视化