当前位置: 首页 > news >正文

Android学习之Provider

在 Android 系统的四大核心组件中,activity负责用户界面交互,service承担后台服务工作,receiver作为广播接收器传递系统或应用间的消息,它们在日常开发中被频繁使用。而content provider虽然使用频率相对较低,但在特定开发场景中,它却是不可或缺的关键角色。为了全面且深入地掌握这个组件,接下来将对其进行更为细致、全面的系统性整理和学习。

什么是 content provider?

content provider是 Android 平台精心设计的一种高效的数据共享机制,它凭借独特的特性,在数据共享领域发挥着重要作用,主要具有以下特点:

1. 跨应用数据共享

这是 Android 中唯一原生支持安全跨应用数据共享的机制,其重要性不言而喻。以通讯录应用为例,它可以通过Content Provider将联系人数据有安全保障地提供给其他应用访问。比如,当某个办公类应用需要获取用户联系人信息进行快速拨号或发送邮件时,就可以通过Content Provider实现,既保证了数据的安全性,又实现了不同应用间的数据交互。

2. 使用场景

  • 私密数据共享:主要适用于需要跨应用共享的私密数据,像联系人信息、短信记录等。这些数据涉及用户隐私,通过Content Provider可以在严格的安全管控下实现共享,确保数据不会被非法访问。

  • 特殊需求场景:当对效率和网络限制有特殊需求时,Content Provider也能大显身手。例如,在一些对网络依赖较小的离线应用中,需要快速访问本地存储的大量数据,Content Provider的本地访问特性可以极大提升数据访问效率。

  • 典型应用场景

    • 访问系统相册:开发图片编辑类应用时,需要获取系统相册中的图片,此时就可以通过Content Provider来实现对相册数据的读取和操作。

    • 获取联系人信息:如前文所述,很多应用需要获取联系人数据用于不同功能。

    • 共享自定义数据库:当多个应用需要共享同一份自定义的结构化数据,比如企业内部的员工信息数据库,就可以借助Content Provider实现数据共享。

3. 工作机制

  • CRUD 操作模式:采用类似数据库的 CRUD(增删改查)操作模式,使得对数据的操作简洁明了且符合开发者的习惯。无论是创建新的数据记录、删除不再需要的数据,还是更新已有数据或查询特定条件的数据,都可以通过相应的操作方法轻松实现。

  • URI 标识数据:通过 URI(统一资源标识符)来标识要操作的数据。每个Content Provider都有一个唯一的 URI,类似于数据库中的表名,通过这个 URI 可以精确地定位到需要操作的数据集合。例如,content://contacts/people就是用于访问联系人数据的 URI。

  • ContentResolver 访问接口:使用ContentResolver作为客户端访问接口。客户端应用通过ContentResolver对象与Content Provider进行交互,ContentResolver会根据 URI 找到对应的Content Provider,并执行相应的操作请求。

4. 与服务端请求的异同

  • 相似之处:都需要通过 URI/URL 定位资源,都支持查询和更新操作。无论是访问Content Provider中的数据,还是向服务端请求数据,都需要明确指定资源的位置,并且都可以对数据进行查询和更新操作。

  • 不同之处Content Provider工作在本地,不需要网络连接,效率更高。例如,获取联系人数据既可通过Content Provider(本地),也可以通过 API 从服务端获取。通过Content Provider在本地直接获取数据,避免了网络请求带来的延迟和不稳定因素,能够快速响应用户的操作;而通过服务端 API 获取数据,则需要依赖网络连接,在网络环境不佳时,数据获取的速度和稳定性都会受到影响。

5. 安全机制

  • 权限系统控制:通过 Android 权限系统控制访问,只有具备相应权限的应用才能访问Content Provider中的数据。例如,要访问联系人数据,应用必须在AndroidManifest.xml文件中声明READ_CONTACTS权限。

  • 读写权限分离:支持读写权限分离,可以分别设置读取数据和写入数据的权限,进一步增强数据的安全性。比如,有些应用只需要读取联系人数据,就可以只授予其读取权限,而不授予写入权限。

  • 精细化 URI 权限控制:可设置精细化的 URI 权限控制,不仅可以对整个Content Provider设置权限,还可以针对具体的 URI 路径设置不同的权限,实现更细致的数据访问控制。

在实际开发中,Content Provider通常与SQLite数据库配合使用,为数据提供统一的访问接口。SQLite数据库负责存储和管理数据,而Content Provider则将这些数据以一种标准化、安全化的方式共享给其他应用。在 Android 10 及更高版本中,Google 对Content Provider的权限控制和安全机制做了进一步强化,以更好地保护用户数据安全。

为什么要使用 Content Provider?

content Provider作为结构化的数据存储和共享方案,同时支持 CRUD 操作和读写分离,对于共享结构化数据来说十分合适。其主要功能在于跨应用间的数据共享,在很多场景下都能发挥重要作用。

一方面,对于应用间共享文件,可以使用FileProvider这种轻量级的封装进行共享。而对于共享结构化数据,Content Provider最大的优势在于支持数据库的 CRUD 操作。它能够将复杂的数据库操作封装起来,以统一的接口提供给其他应用,使得不同应用之间的数据交互更加便捷和规范。

另一方面,我们访问系统的数据也是通过Content Provider进行访问的。系统中的各种数据,如联系人、短信、相册等,都通过Content Provider对外提供访问接口。开发者只需要遵循相应的规则,使用ContentResolver就可以轻松获取和操作这些系统数据,极大地简化了开发过程,提高了开发效率。

开发中为什么不使用 Content Provider?

在实际开发过程中,content provider相较于其它组件使用频率不是特别高。尤其当业务没有对外数据交互需求时,对于应用内的数据共享,使用SharePreferencesRoom等方式显然更加方便。SharePreferences适用于存储简单的键值对数据,如用户的配置信息、登录状态等;Room则是 Google 推出的一款强大的数据库框架,它对SQLite进行了封装,提供了更加简洁、高效的操作方式,非常适合应用内的数据存储和管理。

对于简单数据的传递,自定义广播也能达到相应的要求。自定义广播可以在应用内快速传递消息和简单的数据,实现组件之间的通信。而使用Content Provider则需要自定义实现 CRUD 操作,开发过程相对复杂,需要更多的代码和时间成本。

此外,在 Android 10 以后,Android 对跨应用共享数据有了更严格的要求。例如,在开发中将应用target升到了 Android 10 以上,可能会发现应用无法读取对应 url 的数据,即便使用adb构造查询可以读到数据。这是因为 Android 10 引入了更严格的权限管理机制,应用需要在清单文件中明确声明对目标应用数据的访问权限。最后排查发现,需要在AndroidManifest.xml文件中添加如下代码:

<queries>
<package android:name="com.example.targetapp" />
</queries>

添加上述代码后,应用才能正常读取对应的数据,这无疑增加了开发的难度和复杂性。

如何定义和使用 Content Provider?

定义 Content Provider

  1. 创建 Content Provider 类

继承 ContentProvider 类并实现其核心方法:

import android.content.*
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import android.net.Uriclass MyContentProvider : ContentProvider() {private lateinit var dbHelper: MyDatabaseHelperprivate val uriMatcher = UriMatcher(UriMatcher.NO_MATCH)companion object {const val AUTHORITY = "com.example.myprovider"const val TABLE_NAME = "my_table"val CONTENT_URI: Uri = Uri.parse("content://$AUTHORITY/$TABLE_NAME")}init {uriMatcher.addURI(AUTHORITY, TABLE_NAME, 1)}override fun onCreate(): Boolean {dbHelper = MyDatabaseHelper(context)return true}override fun query(uri: Uri,projection: Array<String>?,selection: String?,selectionArgs: Array<String>?,sortOrder: String?): Cursor? {val db = dbHelper.readableDatabasereturn db.query(TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder)}override fun insert(uri: Uri, values: ContentValues?): Uri? {val db = dbHelper.writableDatabaseval id = db.insert(TABLE_NAME, null, values)context?.contentResolver?.notifyChange(uri, null)return Uri.parse("$CONTENT_URI/$id")}override fun update(uri: Uri,values: ContentValues?,selection: String?,selectionArgs: Array<String>?): Int {val db = dbHelper.writableDatabaseval count = db.update(TABLE_NAME, values, selection, selectionArgs)context?.contentResolver?.notifyChange(uri, null)return count}override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {val db = dbHelper.writableDatabaseval count = db.delete(TABLE_NAME, selection, selectionArgs)context?.contentResolver?.notifyChange(uri, null)return count}override fun getType(uri: Uri): String? {return when (uriMatcher.match(uri)) {1 -> "vnd.android.cursor.dir/vnd.$AUTHORITY.$TABLE_NAME"else -> null}}
}
  1. 注册 Content Provider

在 AndroidManifest.xml 中添加:

<providerandroid:name=".MyContentProvider"android:authorities="com.example.myprovider"android:exported="true"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/file_paths" />
</provider>

使用 Content Provider

  1. 查询数据
val resolver: ContentResolver = context.contentResolver
val uri = Uri.parse("content://com.example.myprovider/my_table")
val cursor = resolver.query(uri, null, null, null, null)cursor?.use {while (it.moveToNext()) {// 处理查询结果}
}
  1. 插入数据
val resolver: ContentResolver = context.contentResolver
val uri = Uri.parse("content://com.example.myprovider/my_table")
val values = ContentValues().apply {put("column1", "value1")put("column2", "value2")
}
val insertedUri = resolver.insert(uri, values)
  1. 更新数据
val resolver: ContentResolver = context.contentResolver
val uri = Uri.parse("content://com.example.myprovider/my_table")
val values = ContentValues().apply {put("column1", "new_value1")
}
val count = resolver.update(uri, values, "column2 = ?", arrayOf("value2"))
  1. 删除数据
val resolver: ContentResolver = context.contentResolver
val uri = Uri.parse("content://com.example.myprovider/my_table")
val count = resolver.delete(uri, "column1 = ?", arrayOf("value1"))

这些步骤展示了如何定义和使用 Content Provider 进行数据操作。开发过程中可根据业务需求修改。

相关文章:

  • linux 黑马 第1-2章
  • 北航自由指令驱动的多模态导航最新研究:OctoNav:开启通用智能体具身导航
  • Python训练营---DAY53
  • yum查看历史操作
  • PDM网络图上的ES、EF、LS、LF是干嘛的怎么计算下一个节点的数值
  • C++中的零拷贝技术
  • 2.线性表的链式存储-链表
  • 掌握产品功能结构图,优化项目开发流程
  • 精粹汇总:大厂编程规范(持续更新)
  • 面向GPU、CPU及机器学习加速器的机器学习编译器
  • 如何关闭WordPress中的评论通知
  • 并发编程-Synchronized
  • WinUI:使用DataGrid控件显示表格
  • 打印机共享问题一键解决,附带设置维护工具
  • 会计-收入-3-关于特定交易的会计处理
  • Power Query动态追加查询(对文件夹下文件汇总)
  • SSM框架实现学生管理系统的需求分析与设计详解
  • 安科瑞亮相2025 SNEC国际太阳能光伏与智慧能源展
  • Mac电脑通过 IntelliJ IDEA 远程连接 MySQL 的详细教程
  • 一个模板元编程示例
  • wordpress置顶没用/seo短视频网页入口引流
  • 个人办公室装修效果图/合肥seo推广外包
  • phpnow搭建本地网站/免费的网页制作软件
  • 免费自建网站步骤/网络营销费用预算
  • 专为网站做点击量/百度推广业务员
  • 靠谱网站建设公司价格多少/沈阳头条今日头条新闻最新消息