ContentProvider #
一、ContentProvider概述 #
ContentProvider(内容提供者)是Android四大组件之一,用于在不同应用之间共享数据。它提供了一套标准的接口,使得一个应用可以访问另一个应用的数据,同时保证数据的安全性。
1.1 ContentProvider的特点 #
- 提供统一的数据访问接口
- 支持跨进程数据共享
- 封装数据存储细节
- 提供权限控制机制
1.2 应用场景 #
- 访问系统数据(联系人、短信、媒体库等)
- 应用间数据共享
- 数据同步
1.3 ContentProvider的注册 #
xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">
<application>
<provider
android:name=".MyProvider"
android:authorities="com.example.myapp.provider"
android:exported="false" />
</application>
</manifest>
二、URI详解 #
2.1 URI结构 #
ContentProvider使用URI(统一资源标识符)来标识数据:
text
content://com.example.myapp.provider/users/1
结构:content://authority/path/id
- content://:固定前缀
- authority:提供者唯一标识
- path:数据路径(表名)
- id:具体记录ID(可选)
2.2 URI示例 #
text
content://com.example.myapp.provider/users # 所有用户
content://com.example.myapp.provider/users/1 # ID为1的用户
content://com.example.myapp.provider/users/# # 任意ID的用户
content://contacts/people # 系统联系人
content://media/external/images/media # 外部存储图片
2.3 UriMatcher #
kotlin
class MyProvider : ContentProvider() {
companion object {
const val AUTHORITY = "com.example.myapp.provider"
val CONTENT_URI: Uri = Uri.parse("content://$AUTHORITY")
const val USERS = 1
const val USER_ID = 2
}
private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
addURI(AUTHORITY, "users", USERS)
addURI(AUTHORITY, "users/#", USER_ID)
}
private fun matchUri(uri: Uri): Int {
return uriMatcher.match(uri)
}
}
三、创建ContentProvider #
3.1 实现ContentProvider #
kotlin
class UserProvider : ContentProvider() {
companion object {
const val AUTHORITY = "com.example.myapp.provider"
val CONTENT_URI: Uri = Uri.parse("content://$AUTHORITY/users")
const val USERS = 1
const val USER_ID = 2
}
private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
addURI(AUTHORITY, "users", USERS)
addURI(AUTHORITY, "users/#", USER_ID)
}
private lateinit var dbHelper: DatabaseHelper
override fun onCreate(): Boolean {
dbHelper = DatabaseHelper(context!!)
return true
}
override fun query(
uri: Uri,
projection: Array<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
sortOrder: String?
): Cursor? {
val db = dbHelper.readableDatabase
val cursor = when (uriMatcher.match(uri)) {
USERS -> {
db.query(
DatabaseHelper.TABLE_USERS,
projection,
selection,
selectionArgs,
null,
null,
sortOrder
)
}
USER_ID -> {
val id = ContentUris.parseId(uri)
db.query(
DatabaseHelper.TABLE_USERS,
projection,
"_id = ?",
arrayOf(id.toString()),
null,
null,
sortOrder
)
}
else -> throw IllegalArgumentException("Unknown URI: $uri")
}
cursor.setNotificationUri(context?.contentResolver, uri)
return cursor
}
override fun getType(uri: Uri): String {
return when (uriMatcher.match(uri)) {
USERS -> "vnd.android.cursor.dir/vnd.example.users"
USER_ID -> "vnd.android.cursor.item/vnd.example.users"
else -> throw IllegalArgumentException("Unknown URI: $uri")
}
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
val db = dbHelper.writableDatabase
return when (uriMatcher.match(uri)) {
USERS -> {
val id = db.insert(DatabaseHelper.TABLE_USERS, null, values)
if (id > 0) {
val newUri = ContentUris.withAppendedId(uri, id)
context?.contentResolver?.notifyChange(newUri, null)
newUri
} else {
null
}
}
else -> throw IllegalArgumentException("Unknown URI: $uri")
}
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
val db = dbHelper.writableDatabase
val count = when (uriMatcher.match(uri)) {
USERS -> {
db.delete(DatabaseHelper.TABLE_USERS, selection, selectionArgs)
}
USER_ID -> {
val id = ContentUris.parseId(uri)
db.delete(DatabaseHelper.TABLE_USERS, "_id = ?", arrayOf(id.toString()))
}
else -> throw IllegalArgumentException("Unknown URI: $uri")
}
if (count > 0) {
context?.contentResolver?.notifyChange(uri, null)
}
return count
}
override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<out String>?
): Int {
val db = dbHelper.writableDatabase
val count = when (uriMatcher.match(uri)) {
USERS -> {
db.update(DatabaseHelper.TABLE_USERS, values, selection, selectionArgs)
}
USER_ID -> {
val id = ContentUris.parseId(uri)
db.update(DatabaseHelper.TABLE_USERS, values, "_id = ?", arrayOf(id.toString()))
}
else -> throw IllegalArgumentException("Unknown URI: $uri")
}
if (count > 0) {
context?.contentResolver?.notifyChange(uri, null)
}
return count
}
}
3.2 MIME类型 #
| 类型 | 格式 | 说明 |
|---|---|---|
| 多条记录 | vnd.android.cursor.dir/vnd.company.type | 如:所有用户 |
| 单条记录 | vnd.android.cursor.item/vnd.company.type | 如:单个用户 |
四、使用ContentProvider #
4.1 ContentResolver #
kotlin
class MainActivity : AppCompatActivity() {
private val contentResolver = contentResolver
fun queryUsers() {
val uri = Uri.parse("content://com.example.myapp.provider/users")
val cursor = contentResolver.query(
uri,
arrayOf("_id", "name", "email"),
null,
null,
"name ASC"
)
cursor?.use {
while (it.moveToNext()) {
val id = it.getLong(it.getColumnIndexOrThrow("_id"))
val name = it.getString(it.getColumnIndexOrThrow("name"))
val email = it.getString(it.getColumnIndexOrThrow("email"))
Log.d("User", "ID: $id, Name: $name, Email: $email")
}
}
}
fun insertUser(name: String, email: String) {
val uri = Uri.parse("content://com.example.myapp.provider/users")
val values = ContentValues().apply {
put("name", name)
put("email", email)
}
val newUri = contentResolver.insert(uri, values)
Log.d("Insert", "New URI: $newUri")
}
fun updateUser(id: Long, name: String) {
val uri = ContentUris.withAppendedId(
Uri.parse("content://com.example.myapp.provider/users"),
id
)
val values = ContentValues().apply {
put("name", name)
}
val count = contentResolver.update(uri, values, null, null)
Log.d("Update", "Updated $count rows")
}
fun deleteUser(id: Long) {
val uri = ContentUris.withAppendedId(
Uri.parse("content://com.example.myapp.provider/users"),
id
)
val count = contentResolver.delete(uri, null, null)
Log.d("Delete", "Deleted $count rows")
}
}
4.2 使用ContentObserver监听数据变化 #
kotlin
class MainActivity : AppCompatActivity() {
private val observer = object : ContentObserver(Handler(Looper.getMainLooper())) {
override fun onChange(selfChange: Boolean) {
super.onChange(selfChange)
// 数据变化时刷新UI
refreshData()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 注册观察者
val uri = Uri.parse("content://com.example.myapp.provider/users")
contentResolver.registerContentObserver(uri, true, observer)
}
override fun onDestroy() {
super.onDestroy()
// 注销观察者
contentResolver.unregisterContentObserver(observer)
}
}
五、访问系统ContentProvider #
5.1 读取联系人 #
kotlin
fun readContacts() {
val cursor = contentResolver.query(
ContactsContract.Contacts.CONTENT_URI,
arrayOf(
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME
),
null,
null,
null
)
cursor?.use {
while (it.moveToNext()) {
val id = it.getLong(it.getColumnIndexOrThrow(ContactsContract.Contacts._ID))
val name = it.getString(it.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME))
// 查询电话号码
val phoneCursor = contentResolver.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
arrayOf(ContactsContract.CommonDataKinds.Phone.NUMBER),
"${ContactsContract.CommonDataKinds.Phone.CONTACT_ID} = ?",
arrayOf(id.toString()),
null
)
phoneCursor?.use { phone ->
while (phone.moveToNext()) {
val number = phone.getString(phone.getColumnIndexOrThrow(
ContactsContract.CommonDataKinds.Phone.NUMBER
))
Log.d("Contact", "Name: $name, Phone: $number")
}
}
}
}
}
5.2 读取媒体库图片 #
kotlin
fun readImages() {
val projection = arrayOf(
MediaStore.Images.Media._ID,
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.DATE_ADDED
)
val cursor = contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection,
null,
null,
"${MediaStore.Images.Media.DATE_ADDED} DESC"
)
cursor?.use {
while (it.moveToNext()) {
val id = it.getLong(it.getColumnIndexOrThrow(MediaStore.Images.Media._ID))
val name = it.getString(it.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME))
val uri = ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
id
)
Log.d("Image", "Name: $name, URI: $uri")
}
}
}
六、权限控制 #
6.1 声明权限 #
xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">
<!-- 声明权限 -->
<permission
android:name="com.example.myapp.provider.READ"
android:label="读取数据"
android:protectionLevel="normal" />
<permission
android:name="com.example.myapp.provider.WRITE"
android:label="写入数据"
android:protectionLevel="dangerous" />
<application>
<provider
android:name=".MyProvider"
android:authorities="com.example.myapp.provider"
android:readPermission="com.example.myapp.provider.READ"
android:writePermission="com.example.myapp.provider.WRITE"
android:exported="true" />
</application>
</manifest>
6.2 使用权限 #
xml
<manifest>
<uses-permission android:name="com.example.myapp.provider.READ" />
<uses-permission android:name="com.example.myapp.provider.WRITE" />
</manifest>
6.3 临时权限授予 #
xml
<provider
android:name=".MyProvider"
android:authorities="com.example.myapp.provider"
android:grantUriPermissions="true"
android:exported="false" />
kotlin
// 临时授权
val uri = Uri.parse("content://com.example.myapp.provider/users/1")
grantUriPermission("com.example.otherapp", uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
七、ContentProvider最佳实践 #
7.1 使用Room + ContentProvider #
kotlin
class UserProvider : ContentProvider() {
private lateinit var database: AppDatabase
override fun onCreate(): Boolean {
database = Room.databaseBuilder(
context!!,
AppDatabase::class.java,
"app.db"
).build()
return true
}
override fun query(
uri: Uri,
projection: Array<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
sortOrder: String?
): Cursor? {
return when (uriMatcher.match(uri)) {
USERS -> {
database.userDao().getAllCursor()
}
else -> null
}
}
}
@Dao
interface UserDao {
@Query("SELECT * FROM users")
fun getAllCursor(): Cursor
}
7.2 使用协程处理异步操作 #
kotlin
class UserProvider : ContentProvider() {
override fun query(
uri: Uri,
projection: Array<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
sortOrder: String?
): Cursor? {
return runBlocking {
withContext(Dispatchers.IO) {
// 异步查询
database.userDao().getAllCursor()
}
}
}
}
八、总结 #
本章详细介绍了ContentProvider:
- ContentProvider的基本概念
- URI的结构和使用
- 创建自定义ContentProvider
- 使用ContentResolver访问数据
- 访问系统ContentProvider
- 权限控制机制
- 最佳实践
ContentProvider是Android跨应用数据共享的核心机制,合理使用可以实现安全、高效的数据共享。
最后更新:2026-03-26