Content Provider — 是一个Android组件,为应用程序之间访问数据提供统一接口。它抽象了物理存储(SQLite、文件、网络资源),并通过ContentResolver确保安全的信息交换。根据Android Developer Guide, 2026,Content Provider 是Android应用程序的四大基本组件之一,与Activity、Service和BroadcastReceiver并列。其任务是在控制读写权限的前提下,使数据可供其他应用程序访问。
要点
Content Provider — 是一个Android组件,管理对集中式数据存储的访问,并通过统一的契约接口将其提供给其他应用程序。它隐藏了存储的实现细节:数据可以存储在SQLite、文件系统、云端,或者是网络请求的结果。
Android包含内置的系统数据Content Provider — ContactsContract、MediaStore、CalendarContract、CallLog。第三方应用程序也可以创建自己的提供程序,用于安全的数据交换。每个提供程序在AndroidManifest.xml中注册,并指定authority — 构成URI第一部分的唯一字符串。
Content Provider遵循客户端-服务器模型工作。提供程序充当服务器,实现六个必需的方法:query、insert、update、delete、getType和onCreate。客户端(另一个应用程序)通过ContentResolver访问提供程序,ContentResolver通过Android的IPC机制将调用传递给提供程序的相应方法。
每个Content Provider由content://方案的URI标识。例如,content://com.example.app.provider/items。第一部分authority(com.example.app.provider)在清单中绑定到提供程序类。/items路径指向表,而/items/5指向ID=5的特定记录。
当应用程序调用ContentResolver.query(URI)时,Android检查调用包的权限,根据authority找到提供程序,如果尚未运行则启动其进程。提供程序执行查询并返回Cursor — 一个包含结果的对象,允许客户端遍历记录。
ContentProvider类要求实现六个抽象方法。每个方法接收URI并返回与操作类型对应的结果。系统可以从任何进程调用这些方法,因此它们必须是线程安全的,并且不能长时间阻塞执行。
query方法接收URI、列数组projection、带参数的selection字符串以及排序顺序。返回包含数据的Cursor。在实现中,需要使用UriMatcher解析URI并对数据库执行相应的SQL查询。
这些方法修改存储中的数据。insert接收ContentValues(键值对)并返回新记录的URI。update和delete接收selection以过滤记录,并返回受影响的行数。修改数据后,提供程序必须通过ContentResolver.notifyChange进行通知。
| 方法 | 用途 | 返回值 |
|---|---|---|
| query | 按URI获取数据 | Cursor或null |
| insert | 添加新记录 | 新记录的URI |
| update | 更新现有记录 | int(行数) |
| delete | 删除记录 | int(行数) |
| getType | URI的MIME类型 | String |
| onCreate | 初始化提供程序 | boolean |
ContentResolver — 是系统中与所有Content Provider工作的统一访问点。客户端应用程序从不直接调用提供程序的方法 — 只通过ContentResolver进行,Android从上下文中获取它。ContentResolver中的CRUD操作与提供程序中的名称相同,但接收URI而不是直接引用。
在提供程序内部,使用UriMatcher解析传入的URI。它允许将URI与数字代码匹配 — 例如,content://authority/items给出代码1,content://authority/items/#给出代码2。这消除了在每个方法中手动解析URI字符串的需要。
// 使用ContentResolver访问联系人
val uri = ContactsContract.Contacts.CONTENT_URI
val cursor = contentResolver.query(
uri,
arrayOf(ContactsContract.Contacts.DISPLAY_NAME),
null, null, null
)
cursor?.use {
while (it.moveToNext()) {
val name = it.getString(it.getColumnIndexOrThrow(
ContactsContract.Contacts.DISPLAY_NAME
))
Log.d("联系人", "姓名:$name")
}
}
Cursor在使用后必须关闭 — 在上面的示例中,use(Kotlin扩展函数)执行此操作。如果Cursor未关闭,将发生内存泄漏,因为它持有对Binder池中数据的引用。对于UI场景,请使用CursorLoader或Room配合LiveData/Flow。
创建自己的Content Provider从继承ContentProvider类开始。提供程序通过SQLiteOpenHelper使用SQLite数据库,并使用UriMatcher确定查询类型。让我们看看管理笔记列表的最小实现。
提供程序在AndroidManifest.xml的application标签内注册。authorities属性设置唯一标识符,exported决定其他应用程序是否可以访问提供程序。没有exported=true,提供程序只能在您的应用程序内部访问。
// 笔记的Content Provider示例
class NotesProvider : ContentProvider() {
companion object {
const val AUTHORITY = "com.example.app.notes"
const val NOTES_PATH = "notes"
const val NOTES_URI = "content://$AUTHORITY/$NOTES_PATH"
const val NOTES_ID = "content://$AUTHORITY/$NOTES_PATH/#"
private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
addURI(AUTHORITY, NOTES_PATH, 1)
addURI(AUTHORITY, "$NOTES_PATH/#", 2)
}
}
override fun query(uri: Uri, projection: Array<String>?,
selection: String?, args: Array<String>?, sort: String?): Cursor? {
return when (uriMatcher.match(uri)) {
1 -> dbHelper.readableDatabase.query(TABLE_NOTES,
projection, selection, args, null, null, sort)
2 -> dbHelper.readableDatabase.query(TABLE_NOTES,
projection, "_id=?", arrayOf(uri.lastPathSegment), null, null, null)
else -> throw IllegalArgumentException("Unknown URI: $uri")
}
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
val id = dbHelper.writableDatabase.insert(TABLE_NOTES, null, values)
context?.contentResolver?.notifyChange(uri, null)
return ContentUris.withAppendedId(uri, id)
}
override fun delete(uri: Uri, selection: String?, args: Array<String>?): Int {
val count = dbHelper.writableDatabase.delete(TABLE_NOTES, selection, args)
context?.contentResolver?.notifyChange(uri, null)
return count
}
// getType、update、onCreate为简洁已省略
}
创建类后,需要在清单中使用android:authorities和android:exported(如果提供程序是公开的则为true)属性注册提供程序。系统在第一次访问时创建提供程序实例 — 这发生在UI线程中,因此onCreate应该快速执行。
Content Provider允许在两层上管理数据访问:读取权限和写入权限。它们在清单中使用android:readPermission和android:writePermission属性设置。如果客户端应用程序没有相应的权限,系统将拒绝调用并抛出SecurityException。
Android通过FLAG_GRANT_READ_URI_PERMISSION和FLAG_GRANT_WRITE_URI_PERMISSION标志支持临时权限。当应用程序通过Intent将文件的URI传递给另一个应用程序时,这很有用 — 接收方只能在有限时间内访问特定的URI。系统在接收方应用程序完成后撤销临时权限。
对于系统提供程序,Android要求在应用程序清单中指定具体的权限。例如,访问联系人需要READ_CONTACTS,访问日历需要READ_CALENDAR。从Android 6开始,这些权限在运行时请求,而不是在安装时。
常见问题
Content Provider — 是一个Android组件,通过ContentResolver为应用程序之间提供标准的数据交换接口。它抽象了存储方式(SQLite、文件、网络),并通过读写权限控制提供安全的数据访问。
Authority — 是在AndroidManifest.xml中指定的提供程序唯一标识字符串。它构成URI content://authority/path的第一部分,系统用它来将ContentResolver调用路由到正确的提供程序。Authority必须在设备上的所有应用程序中唯一。
UriMatcher将URI与数字代码匹配。您通过addURI添加模式,然后调用match为传入的URI获取代码。这允许在query、insert、update、delete方法中确定请求的是哪个表或记录,并对数据库执行相应的操作。
是的,Cursor在使用后必须关闭。如果Cursor未关闭,将发生内存泄漏,因为它持有对提供程序数据的Binder引用。在Kotlin中,使用use函数自动关闭;在Java中,使用try-with-resources或在finally块中调用cursor.close()。
Content Provider — 是用于应用程序间数据访问的组件,而SQLiteDatabase是单个应用程序的内部存储。Content Provider提供URI接口和权限控制,而SQLiteDatabase直接与数据库一起工作,没有操作系统级别的安全机制。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。