You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

仅掌握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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 03:15:03