仅掌握Content Provider基础定义,求其实时使用场景的实操示例
实操示例1:读取系统通讯录数据(最常用跨进程场景)
这是最典型的Content Provider落地场景:系统通讯录的所有联系人数据由系统进程的Contacts Provider统一管理,第三方应用不需要获得通讯录进程的直接访问权限,仅需申请对应权限即可调用标准接口读写数据,完全贴合Content Provider跨进程数据共享、权限管控、数据封装的核心特性。
步骤1:添加访问权限
在应用的AndroidManifest.xml中添加读取通讯录的权限声明:
<uses-permission android:name="android.permission.READ_CONTACTS" />
Android 6.0及以上版本需要额外动态申请该运行时权限,为常规权限申请流程。
步骤2:编写读取联系人核心代码
// 定义需要查询的联系人字段:ID、姓名、手机号 val projection = arrayOf( ContactsContract.CommonDataKinds.Phone._ID, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER ) // 通过ContentResolver访问系统Contacts Provider contentResolver.query( ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projection, null, null, "${ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME} ASC" )?.use { cursor -> // 遍历游标读取所有联系人数据 val nameIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME) val numberIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER) while (cursor.moveToNext()) { val name = cursor.getString(nameIndex) val phoneNumber = cursor.getString(numberIndex) Log.d("ContactInfo", "联系人:$name,电话:$phoneNumber") } }
该场景下你完全不需要知道系统通讯录的存储方式、内部逻辑,仅通过Provider暴露的标准接口就能拿到所需数据,就是Content Provider封装能力的直接体现。
实操示例2:自定义Content Provider实现跨应用数据共享
如果你开发的多个应用需要共享数据,例如记账应用和配套的报表应用,需要让报表应用只读访问记账应用的账单数据,就可以通过自定义Content Provider实现。
步骤1:在数据提供方(记账应用)自定义Provider
class BillProvider : ContentProvider() { // Provider底层依赖的SQLite数据库辅助类 private lateinit var dbHelper: BillDbHelper companion object { // 定义Provider的全局唯一标识AUTHORITY,通常用应用包名作为前缀 const val AUTHORITY = "com.example.billapp.BillProvider" // 账单数据对应的访问URI val BILL_CONTENT_URI = Uri.parse("content://$AUTHORITY/bills") // URI匹配器,区分不同的访问请求类型 private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply { addURI(AUTHORITY, "bills", 1) // 匹配所有账单的查询请求 addURI(AUTHORITY, "bills/#", 2) // 匹配单个账单的查询请求,#为数字通配符 } } override fun onCreate(): Boolean { dbHelper = BillDbHelper(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 return when(uriMatcher.match(uri)) { 1 -> db.query("bills", projection, selection, selectionArgs, null, null, sortOrder) 2 -> { val billId = uri.lastPathSegment!! db.query("bills", projection, "_id = ?", arrayOf(billId), null, null, sortOrder) } else -> throw IllegalArgumentException("未知访问URI:$uri") } } // 增删改接口不对外开放,直接返回空或0,避免外部修改内部数据 override fun insert(uri: Uri, values: ContentValues?): Uri? = null override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?) = 0 override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>?) = 0 override fun getType(uri: Uri): String? = null }
步骤2:在记账应用的Manifest中注册Provider
<provider android:name=".BillProvider" android:authorities="com.example.billapp.BillProvider" android:exported="true" android:readPermission="com.example.billapp.permission.READ_BILL" /> <!-- 自定义账单读取权限,只有申请该权限的应用才能访问数据 --> <permission android:name="com.example.billapp.permission.READ_BILL" android:protectionLevel="normal" />
步骤3:在数据访问方(报表应用)读取账单数据
首先在报表应用的Manifest中申请自定义权限:
<uses-permission android:name="com.example.billapp.permission.READ_BILL" />
代码中调用接口查询账单数据:
contentResolver.query( Uri.parse("content://com.example.billapp.BillProvider/bills"), arrayOf("id", "amount", "time", "type"), null, null, "time DESC" )?.use { cursor -> while (cursor.moveToNext()) { val amount = cursor.getDouble(cursor.getColumnIndexOrThrow("amount")) Log.d("BillData", "账单金额:$amount") } }
该场景下可以通过Provider的权限定义、方法重写,精确控制外部对内部数据的访问范围,完全符合你已知的Provider核心特性。
核心作用梳理
- 统一跨进程数据共享规范:不管是系统服务还是第三方应用的数据共享,都遵循同一套接口标准,不需要各自定制跨进程通信逻辑
- 数据封装:底层存储逻辑(SQLite、本地文件、甚至网络缓存)完全由Provider侧控制,调用方不需要关心实现细节
- 安全管控:通过权限定义、URI匹配规则,可以实现细粒度的数据读写权限控制,避免内部数据直接暴露
内容的提问来源于stack exchange,提问作者Dhara Vamja
相关产品推荐
相关产品推荐

