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 和数据库结构。

相关推荐
实名上网宋凯宣7 小时前
Django
后端·python·django
Csvn8 小时前
WASM
前端
利威尔xu9 小时前
Go IM 源码实战(14):本地缓存快 3000 倍,失效却难 10 倍
后端·缓存·微服务·面试·golang
修己xj9 小时前
我用 AI 给自己写了一个 Markdown 编辑器,然后把它开源了
前端
FRing039 小时前
vue2干货总结2(v-bind、v-for、v-model、指令修饰符、v-bind操作class和style)
开发语言·前端·vue.js·vue2
幸运小猿10 小时前
模拟慢上传-处理超时重试image 丢失线上问题
前端·javascript·html
逃逸线LOF10 小时前
Ribbon负载均衡原理(Irule组件、源码分析)
后端·spring cloud·ribbon
慢云智慧空间10 小时前
从设备智能到空间理解,空间智能终端解决方案商如何重新定义行业标准
后端
利威尔xu10 小时前
Go IM 源码实战(13):30 个会话只有 2 个红点,未读数错在三条路径
redis·后端·微服务·面试·golang
薛定谔的猫-菜鸟程序员11 小时前
软件架构设计师开发资料大全(从入门到精通)
后端·python·系统架构·flask·架构设计师