Content Provider 是 Android 四大核心组件之一,用于跨应用数据共享 和数据抽象化管理 。它通过标准化的接口(基于 URI)封装数据访问逻辑,支持跨进程安全通信。本文从核心功能 、使用场景 、版本兼容 、代码实现 及注意事项五个维度全面解析。
一、核心功能与使用场景
功能 | 使用场景 |
---|---|
跨应用数据共享 | 应用 A 公开数据(如用户配置、数据库)供应用 B 读取或修改。 |
统一数据访问接口 | 封装复杂数据源(如 SQLite、文件、网络数据)为统一 API,简化调用方逻辑。 |
系统数据访问 | 访问系统提供的 Content Provider(如联系人、媒体库、日历事件)。 |
数据安全控制 | 通过权限机制限制其他应用对敏感数据的访问(如 android:readPermission )。 |
二、基础使用与代码示例
1、创建自定义 Content Provider
-
步骤:
- 继承
ContentProvider
并实现onCreate()
、query()
、insert()
、update()
、delete()
和getType()
。 - 在
AndroidManifest.xml
中声明 Provider。
- 继承
-
代码示例(Kotlin) :
kotlinclass MyProvider : ContentProvider() { private lateinit var dbHelper: MyDbHelper override fun onCreate(): Boolean { dbHelper = MyDbHelper(context!!) return true } override fun query( uri: Uri, projection: Array<String>?, selection: String?, selectionArgs: Array<String>?, sortOrder: String? ): Cursor? { val db = dbHelper.readableDatabase val cursor = db.query("my_table", projection, selection, selectionArgs, null, null, sortOrder) cursor.setNotificationUri(context!!.contentResolver, uri) return cursor } // 实现其他方法(insert/update/delete/getType)... }
-
Manifest 声明:
xml<provider android:name=".MyProvider" android:authorities="com.example.myapp.provider" android:exported="true" <!-- 是否允许跨应用访问 --> android:readPermission="com.example.permission.READ_DATA" />
2、访问 Content Provider
-
通过 ContentResolver:
kotlinval uri = Uri.parse("content://com.example.myapp.provider/my_table") val cursor = contentResolver.query( uri, arrayOf("id", "name"), // 查询的列 "age > ?", // WHERE 条件 arrayOf("18"), // 参数值 "name DESC" // 排序 ) cursor?.use { while (it.moveToNext()) { val id = it.getInt(it.getColumnIndex("id")) val name = it.getString(it.getColumnIndex("name")) } }
3、访问系统 Content Provider
-
示例:读取联系人信息:
kotlinval contactsUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI val projection = arrayOf( ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER ) contentResolver.query(contactsUri, projection, null, null, null)?.use { cursor -> while (cursor.moveToNext()) { val name = cursor.getString(0) val phone = cursor.getString(1) } }
三、版本兼容性问题
版本 | 关键变更 | 适配方案 |
---|---|---|
Android 6.0+ | 运行时权限(如 READ_CONTACTS )需动态申请。 |
调用系统 Provider 前检查权限: ContextCompat.checkSelfPermission() + ActivityResultLauncher 。 |
Android 7.0+ | 禁止通过 file:// URI 直接共享文件,需使用 FileProvider 。 |
使用 FileProvider 生成 content:// URI: 参考 FileProvider.getUriForFile() 。 |
Android 10+ | 分区存储(Scoped Storage)限制直接访问外部存储文件路径。 | 通过 MediaStore 或 Storage Access Framework 访问媒体文件。 |
Android 11+ | 强制分区存储,requestLegacyExternalStorage 标志失效。 |
使用 MediaStore 或申请 MANAGE_EXTERNAL_STORAGE 权限(需上架审核)。 |
四、注意事项与最佳实践
1、URI 设计规范
-
路径匹配 :使用
UriMatcher
管理 URI 路由。kotlinprivate val uriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply { addURI("com.example.provider", "books", BOOKS) addURI("com.example.provider", "books/#", BOOK_ID) } override fun getType(uri: Uri): String? { return when (uriMatcher.match(uri)) { BOOKS -> "vnd.android.cursor.dir/vnd.example.books" BOOK_ID -> "vnd.android.cursor.item/vnd.example.books" else -> throw IllegalArgumentException("Unknown URI: $uri") } }
2、数据安全
-
权限控制 :在 Manifest 中声明
android:permission
或细化到方法级别。xml<provider android:name=".SecureProvider" android:authorities="com.example.secure.provider" android:writePermission="com.example.permission.WRITE_DATA" />
-
防止 SQL 注入:使用参数化查询,避免拼接 SQL 语句。
kotlin// 错误方式(易受注入攻击) val selection = "id = $userInput" // 正确方式 val selection = "id = ?" val selectionArgs = arrayOf(userInput)
3、性能优化
-
异步查询 :避免在主线程执行耗时操作,使用
CursorLoader
或协程。kotlin// 使用协程(Room + Kotlin Flow) @Dao interface BookDao { @Query("SELECT * FROM books") fun getAllBooks(): Flow<List<Book>> }
-
批量操作 :使用
ContentProviderOperation
提升批量写入效率。kotlinval operations = ArrayList<ContentProviderOperation>().apply { add(ContentProviderOperation.newInsert(uri) .withValue("title", "Book 1") .build()) add(ContentProviderOperation.newInsert(uri) .withValue("title", "Book 2") .build()) } contentResolver.applyBatch(authority, operations)
4、生命周期与资源释放
- 关闭 Cursor :使用
Cursor.close()
或use{}
扩展函数防止内存泄漏。 - 注册观察者 :通过
ContentResolver.registerContentObserver()
监听数据变化。
kotlin
val observer = object : ContentObserver(Handler(Looper.getMainLooper())) {
override fun onChange(selfChange: Boolean, uri: Uri?) {
// 数据变化时刷新 UI
}
}
contentResolver.registerContentObserver(uri, true, observer)
// 在 onDestroy 中注销
override fun onDestroy() {
contentResolver.unregisterContentObserver(observer)
}
五、典型使用场景
场景 | 实现方案 |
---|---|
跨应用共享数据库 | 自定义 Content Provider 暴露 SQLite 表的 CRUD 操作。 |
文件共享 | 使用 FileProvider 生成安全的 content:// URI,替代 file:// 。 |
与 SyncAdapter 集成 | 通过 Content Provider 同步远程服务器数据与本地存储(如邮件客户端)。 |
统一访问多数据源 | 封装 REST API、数据库、文件系统为统一接口,简化客户端调用。 |
六、总结
需求 | 推荐方案 |
---|---|
简单数据共享 | 自定义 Content Provider + 权限控制。 |
文件共享 | FileProvider (适配 Android 7.0+)。 |
访问系统数据(联系人、媒体) | 使用系统 Provider + 动态权限申请。 |
高性能数据操作 | CursorLoader + 异步处理(如协程、Room)。 |
通过合理设计 URI 结构、适配版本限制、优化查询性能并严格管控权限,可高效利用 Content Provider 实现安全、灵活的数据共享与管理。