andorid 学习之ContentProvider 和 ContentResolver 使用笔记

📚 概述

这个教程将帮助你理解 Android 中的ContentProviderContentResolver,它们是 Android 四大组件之一,用于实现应用间的数据共享。

🎯 学习目标

  1. 理解 ContentProvider 和 ContentResolver 的作用
  2. 学会创建和注册 ContentProvider
  3. 学会使用 ContentResolver 访问数据
  4. 理解 Uri 的作用和格式

🔍 核心概念

1. ContentProvider(内容提供者)

作用:暴露应用数据给其他应用访问

类比:就像一个餐厅的服务员,负责提供食物(数据)给客人(其他应用)

位置:在 chapter06 模块中(数据提供方)

2. ContentResolver(内容解析器)

作用:访问 ContentProvider 提供的数据

类比:就像一个客人,通过服务员(ContentProvider)点餐(获取数据)

位置:在 chapter07 模块中(数据访问方)

3. Uri(统一资源标识符)

作用:定位要访问的数据,类似网址

格式content://授权标识/数据路径/ID

示例

content://com.example.chapter06.provider.ShoppingCarProvider/shoppingcar ↓ ↓ 授权标识(类似域名) 数据路径

🏗️ 实现步骤

第一步:创建 ContentProvider(在 chapter06 中)

我已经为你创建了ShoppingCarProvider.java,它包含以下核心方法:

1.onCreate()- 初始化
@Override public boolean onCreate() { // 初始化数据库访问对象 shoppingCarDao = MyApplication.shoppingDatabase.shoppingCarDao(); return true; }
2.query()- 查询数据
@Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { // 根据 Uri 返回对应的数据 // 返回值是 Cursor(游标),包含查询结果 }
3.insert()- 插入数据
@Override public Uri insert(Uri uri, ContentValues values) { // 插入数据到数据库 // 返回新插入数据的 Uri }
4.update()- 更新数据
@Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { // 更新数据 // 返回受影响的行数 }
5.delete()- 删除数据
@Override public int delete(Uri uri, String selection, String[] selectionArgs) { // 删除数据 // 返回删除的行数 }

第二步:注册 ContentProvider(在 AndroidManifest.xml 中)

chapter06/src/main/AndroidManifest.xml中添加:

<provider android:name=".provider.ShoppingCarProvider" android:authorities="com.example.chapter06.provider.ShoppingCarProvider" android:exported="true" />

关键属性说明

  • android:name:Provider 的类名
  • android:authorities:授权标识(必须全局唯一)
  • android:exported="true":允许其他应用访问

第三步:声明包可见性(Android 11+ 必需)

重要提示:从 Android 11 (API 30) 开始,应用需要显式声明要访问的其他应用!

chapter07/src/main/AndroidManifest.xml中添加:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <!-- Android 11+ 包可见性声明(必需!) 如果不添加,会报错:Failed to find provider info --> <queries> <!-- 方式1:通过包名声明要访问的应用 --> <package android:name="com.example.chapter06" /> <!-- 方式2:通过 provider 的 authorities 声明(更精确)--> <provider android:authorities="com.example.chapter06.provider.ShoppingCarProvider" /> </queries> <application> ... </application> </manifest>

关键点

  • <queries>标签必须放在<application>标签之前
  • 两种声明方式可以同时使用,也可以只用一种
  • 这是 Android 11 引入的隐私和安全特性

第四步:使用 ContentResolver 访问数据(在 chapter07 中)

ProviderActivity.java中,我已经实现了三个示例方法:

1. 查询所有数据 -readAllShoppingCar()
// 1. 获取 ContentResolver ContentResolver contentResolver = getContentResolver(); // 2. 定义 Uri Uri uri = Uri.parse("content://com.example.chapter06.provider.ShoppingCarProvider/shoppingcar"); // 3. 查询数据 Cursor cursor = contentResolver.query(uri, null, null, null, null); // 4. 遍历结果 while (cursor.moveToNext()) { int id = cursor.getInt(cursor.getColumnIndex("_id")); String name = cursor.getString(cursor.getColumnIndex("name")); // ... 读取其他字段 } // 5. 关闭游标 cursor.close();
2. 插入数据 -insertTestData()
// 1. 准备数据 ContentValues values = new ContentValues(); values.put("name", "测试商品"); values.put("price", 99.99f); values.put("count", 1); // 2. 插入数据 Uri newUri = contentResolver.insert(uri, values);
3. 查询单条数据 -queryShoppingCarById()
// 1. 构建包含 ID 的 Uri Uri queryUri = Uri.withAppendedPath(uri, "1"); // 2. 查询数据 Cursor cursor = contentResolver.query(queryUri, null, null, null, null); // 3. 读取第一条数据 if (cursor.moveToFirst()) { String name = cursor.getString(cursor.getColumnIndex("name")); }

📊 数据流程图

┌─────────────────────────────────────────────────────────────┐ │ 数据共享流程 │ └─────────────────────────────────────────────────────────────┘ chapter06 模块(数据提供方) chapter07 模块(数据访问方) ┌──────────────────────┐ ┌──────────────────────┐ │ ShoppingDatabase │ │ ProviderActivity │ │ (购物车数据库) │ │ │ └──────────┬───────────┘ └──────────┬───────────┘ │ │ ↓ ↓ ┌──────────────────────┐ ┌──────────────────────┐ │ ShoppingCarProvider │ │ ContentResolver │ │ (ContentProvider) │ │ (内容解析器) │ │ │ │ │ │ - onCreate() │ │ - query() │ │ - query() │ ←───── Uri ──────────│ - insert() │ │ - insert() │ │ - update() │ │ - update() │ │ - delete() │ │ - delete() │ │ │ └──────────────────────┘ └──────────────────────┘ ↑ ↑ │ │ 在 AndroidManifest.xml 中注册 添加 <queries> 标签声明 授权标识:xxx.ShoppingCarProvider (Android 11+ 必需)

🔑 关键知识点

1. Uri 的作用

Uri 就像数据的"地址",告诉系统要访问哪里的数据。

格式content://授权标识/数据路径/ID(可选)

示例

content://com.example.chapter06.provider.ShoppingCarProvider/shoppingcar → 访问所有购物车数据 content://com.example.chapter06.provider.ShoppingCarProvider/shoppingcar/1 → 访问 ID=1 的购物车数据

2. Cursor(游标)

Cursor 类似于一个指针,用于遍历查询结果。

常用方法

  • moveToNext():移动到下一行,返回 true 表示还有数据
  • moveToFirst():移动到第一行
  • getColumnIndex("列名"):获取列的索引
  • getInt(索引):获取整数值
  • getString(索引):获取字符串值
  • close():关闭游标,释放资源

使用示例

Cursor cursor = contentResolver.query(uri, null, null, null, null); while (cursor.moveToNext()) { int id = cursor.getInt(cursor.getColumnIndex("_id")); String name = cursor.getString(cursor.getColumnIndex("name")); } cursor.close();

3. ContentValues

ContentValues 用于存储要插入或更新的数据,类似于 HashMap。

使用示例

ContentValues values = new ContentValues(); values.put("name", "商品名称"); // 键值对 values.put("price", 99.99f); values.put("count", 1);

4. UriMatcher(Uri 匹配器)

UriMatcher 用于匹配不同的 Uri,判断客户端请求的是什么数据。

定义匹配规则

private static UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); static { // 匹配:content://.../shoppingcar uriMatcher.addURI(AUTHORITY, "shoppingcar", SHOPPING_CAR_ALL); // 匹配:content://.../shoppingcar/1(# 表示数字) uriMatcher.addURI(AUTHORITY, "shoppingcar/#", SHOPPING_CAR_ITEM); }

使用匹配器

int match = uriMatcher.match(uri); switch (match) { case SHOPPING_CAR_ALL: // 查询所有数据 break; case SHOPPING_CAR_ITEM: // 查询单条数据 break; }

🎮 如何测试

第一步:准备数据(在 chapter06 中)

  1. 运行 chapter06 模块
  2. GoodsList页面中添加一些商品到购物车
  3. 这些数据会保存在ShoppingDatabase

第二步:访问数据(在 chapter07 中)

  1. 运行 chapter07 模块
  2. 点击"读取 chapter06 的购物车数据"按钮
  3. 你会看到从 chapter06 读取的购物车数据

第三步:测试其他功能

  1. 点击"向 chapter06 插入测试数据":插入一条测试数据
  2. 点击"查询 ID=1 的数据":查询单条数据
  3. 再次点击"读取购物车数据":查看插入的数据

💡 常见问题

1. 为什么会报 "Failed to find provider info" 错误?(重要!)

错误信息

Failed to find provider info for com.example.chapter06.provider.ShoppingCarProvider

原因:Android 11 (API 30) 及以上版本的包可见性限制

解决方法: 在chapter07/src/main/AndroidManifest.xml中添加<queries>标签:

<queries> <package android:name="com.example.chapter06" /> <provider android:authorities="com.example.chapter06.provider.ShoppingCarProvider" /> </queries>

详细说明

  • Android 11 之前:应用可以自由查询其他应用
  • Android 11 及以后:必须显式声明要访问的应用
  • 目的:增强用户隐私和应用安全性

2. 为什么查询不到数据?

可能原因

  • ❌ chapter06 应用未安装(必须先安装)
  • ❌ chapter06 从未运行过(至少运行一次,初始化数据库)
  • ❌ ContentProvider 未在 AndroidManifest.xml 中注册
  • ❌ Uri 写错了(授权标识或路径错误)
  • ❌ chapter06 的购物车数据库中没有数据
  • 缺少<queries>标签(Android 11+)

解决方法

  1. 先安装并运行 chapter06,添加购物车数据
  2. 检查 chapter06 的 AndroidManifest.xml 中的<provider>配置
  3. 确认 Uri 的授权标识和 Provider 中定义的一致
  4. 确保 chapter07 的 AndroidManifest.xml 中有<queries>标签

3. 为什么会报 SecurityException?

可能原因

  • ContentProvider 的android:exported设置为false
  • 需要权限但没有声明

解决方法

  • 在 chapter06 的 AndroidManifest.xml 中设置android:exported="true"

4. ContentProvider 闪退:NullPointerException

错误信息

NullPointerException: Attempt to invoke virtual method '...shoppingCarDao()' on a null object reference

原因:ContentProvider 的onCreate()在 Application 的onCreate()之前调用

解决方法:使用延迟初始化

private ShoppingCarDao getShoppingCarDao() { if (shoppingCarDao == null) { MyApplication app = (MyApplication) getContext().getApplicationContext(); shoppingCarDao = app.getShoppingDB().shoppingCarDao(); } return shoppingCarDao; }

5. Room 和 ContentProvider 有什么区别?

Room

  • 用于应用内部的数据库操作
  • 更简单、更高效
  • 不能跨应用访问

ContentProvider

  • 用于跨应用的数据共享
  • 更复杂,需要转换数据格式(Entity → Cursor)
  • 可以让其他应用访问你的数据

🎓 学习总结

数据提供方(chapter06)需要做的:

  1. ✅ 创建 ContentProvider 类
  2. ✅ 实现 query、insert、update、delete 方法
  3. ✅ 在 AndroidManifest.xml 中注册 Provider
  4. ✅ 定义 Uri(授权标识 + 数据路径)

数据访问方(chapter07)需要做的:

  1. 添加<queries>标签(Android 11+ 必需)
  2. ✅ 获取 ContentResolver 实例
  3. ✅ 定义要访问的 Uri(与提供方一致)
  4. ✅ 调用 ContentResolver 的方法访问数据
  5. ✅ 处理返回的 Cursor 数据

📝 代码位置

chapter06(数据提供方)

  • ContentProvider:chapter06/src/main/java/com/example/chapter06/provider/ShoppingCarProvider.java
  • AndroidManifest:chapter06/src/main/AndroidManifest.xml(注册 Provider)
  • 数据库:ShoppingDatabaseShoppingCarDao

chapter07(数据访问方)

  • Activity:chapter07/src/main/java/com/example/chapter07/ProviderActivity.java
  • 布局:chapter07/src/main/res/layout/activity_provider_activety.xml
  • AndroidManifest:chapter07/src/main/AndroidManifest.xml(包含<queries>标签)

🚀 下一步

现在你可以:

  1. 运行 chapter07 模块,测试数据访问功能
  2. 查看日志(Logcat),观察数据交互过程
  3. 尝试修改代码,添加更新和删除功能
  4. 理解 ContentProvider 的工作原理

⚠️ 重要提醒

Android 版本差异

Android 版本API Level是否需要<queries>说明
Android 10 及以下≤ 29❌ 不需要可以自由访问其他应用
Android 11 及以上≥ 30✅ 必需必须添加<queries>标签

完整配置清单

chapter06(数据提供方)
<application> <!-- 注册 ContentProvider --> <provider android:name=".provider.ShoppingCarProvider" android:authorities="com.example.chapter06.provider.ShoppingCarProvider" android:exported="true" /> </application>
chapter07(数据访问方)
<!-- Android 11+ 必需 --> <queries> <package android:name="com.example.chapter06" /> <provider android:authorities="com.example.chapter06.provider.ShoppingCarProvider" /> </queries> <application> <!-- Activities --> </application>

祝学习顺利!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1120358.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python基于django的群众网上高效办事系统的设计与实现_6e4j9xi1

目录基于Django的群众网上高效办事系统设计与实现关于博主开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;基于Django的群众网上高效办事系统设计与实现 该系统旨在利用Django框架构建一个高…

python基于django的食品仓库管理系统_2i4gc8z0

目录食品仓库管理系统概述核心功能模块技术实现要点扩展性与优化关于博主开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;食品仓库管理系统概述 基于Django框架的食品仓库管理系统旨在实现食…

python基于django的企业人力资源招聘管理系统_fsjuwx26

目录基于Django的企业人力资源招聘管理系统概述系统功能模块技术实现特点关于博主开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;基于Django的企业人力资源招聘管理系统概述 该系统采用Pyt…

【Python】五大数据容器之间的区别

1、Python五大数据容器及其方法容器类型元素存储方式有哪些方法List列表以[]存储多个元素index、insert、append、extend、del、pop、remove、clear、count、reverse、sort等方法Tuple元组以()存储元素index、count、len方法Str字符串以""存储字符index、replace、sp…

深度学习毕设项目推荐-基于CNN深度学习的遥感图片识别沙漠湖泊和森林基于CNN深度学习的遥感图片识别沙漠湖泊和森林

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

python基于django的汽车租赁买卖管理系统_189h7k1a

目录汽车租赁买卖管理系统概述核心功能模块技术实现亮点系统优势关于博主开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;汽车租赁买卖管理系统概述 该系统基于Django框架开发&#xff0c;旨…

python基于django的申家沟村务管理系统_村委会管理系统3bm52uvo

目录项目背景技术架构核心功能创新点应用价值关于博主开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;项目背景 申家沟村务管理系统基于Django框架开发&#xff0c;旨在实现村委会工作的数字…

深度学习毕设项目推荐-基于CNN卷积网络的蔬菜识别基于深度学习卷积网络的蔬菜识别

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

python基于django的社区健身器材报修系统 公园管理系统_g9741947

目录基于Django的社区健身器材报修系统与公园管理系统关于博主开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;基于Django的社区健身器材报修系统与公园管理系统 该系统旨在通过数字化手段提…

HTTP 错误码

500 internal server error这通常是服务器内部错误&#xff0c;如代码有问题503 service unavailable通常由于服务器过载&#xff0c;不能响应当前的请求&#xff0c;简单点说&#xff0c;用户请求数(并发)过大&#xff0c;超过了服务器的限制&#xff0c;服务器拒绝了用户的请…

毕业论文AI率太高怎么办?降ai率从80%降到15%!免费降ai率工具实测。

国庆假期一过&#xff0c;又到“论文人”的修罗场。朋友们&#xff0c;你是不是也被AIGC检测折磨过&#xff1f; 明明一行一字都自己写的&#xff0c;结果检测报告红得像过年&#xff1a;AI率99%&#xff01;那一刻真的想原地删库跑路。 不过话说回来&#xff0c;这两年确实有…

学长亲荐2026研究生AI论文工具TOP10:开题文献综述全攻略

学长亲荐2026研究生AI论文工具TOP10&#xff1a;开题文献综述全攻略 2026年研究生AI论文工具测评&#xff1a;精准匹配学术需求的实用指南 随着人工智能技术在学术领域的深度渗透&#xff0c;越来越多的研究生开始依赖AI工具提升论文写作效率。然而&#xff0c;面对市场上琳琅满…

python基于django的社区团购系统_0d5k06f6

目录 社区团购系统概述核心功能模块技术实现要点扩展性与优化 关于博主开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01; 社区团购系统概述 基于Django的社区团购系统是一个结合电子商务与社…

【k8s设置污点/容忍】

背景&#xff1a; 一个应用占用的资源很大&#xff0c;而节点不是很大时&#xff0c;需要让这个应用单独占据一个节点&#xff0c;不让别的应用调度到这个节点上&#xff0c;从而避免业务高峰时资源不够在node上设置污点kubectl taint nodes node-1 dedicatedadmin-server:NoSc…

2026毕业生必看!4个实测降ai率工具4,教你如何利用ai降ai技巧,轻松实现免费降低ai率。

写论文最怕什么&#xff1f;不是写不出来&#xff0c;而是写出来被系统一测&#xff0c;AI率高得吓人。明明自己花了不少心思&#xff0c;结果报告上红彤彤一片&#xff0c;动不动就是 80% 以上。别提多崩溃了。 其实呢&#xff0c;想要顺利过关&#xff0c;关键是要学会降低ai…

4款亲测免费降ai率工具推荐!3分钟把论文降ai检测值降到10%以下,附aigc免费降重全教程。

写论文最怕什么&#xff1f;不是写不出来&#xff0c;而是写出来被系统一测&#xff0c;AI率高得吓人。明明自己花了不少心思&#xff0c;结果报告上红彤彤一片&#xff0c;动不动就是 80% 以上。别提多崩溃了。 其实呢&#xff0c;想要顺利过关&#xff0c;关键是要学会降低ai…

python基于django的人力资源管理系统_企业员工信息管理系统 主管lod65og9

目录 系统概述核心功能模块技术特点应用价值 关于博主开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01; 系统概述 Python基于Django的人力资源管理系统&#xff08;企业员工信息管理系统&…

2026最新深度测评!4款亲测有效的降ai率工具,帮你免费降ai率,知网维普aigc免费降重轻松过关。

写论文最怕什么&#xff1f;不是写不出来&#xff0c;而是写出来被系统一测&#xff0c;AI率高得吓人。明明自己花了不少心思&#xff0c;结果报告上红彤彤一片&#xff0c;动不动就是 80% 以上。别提多崩溃了。 其实呢&#xff0c;想要顺利过关&#xff0c;关键是要学会降低ai…

最新降AI工具推荐!真正实现aigc免费降重,这才是目前最好用的降ai率工具。

写论文最怕什么&#xff1f;不是写不出来&#xff0c;而是写出来被系统一测&#xff0c;AI率高得吓人。明明自己花了不少心思&#xff0c;结果报告上红彤彤一片&#xff0c;动不动就是 80% 以上。别提多崩溃了。 其实呢&#xff0c;想要顺利过关&#xff0c;关键是要学会降低ai…

普通专、本科学不了网络安全?最有效的自学方法我替你找到了!

目录前言自学网安第一阶段&#xff1a;打牢基础学习这些基础知识有什么用呢&#xff1f;第二阶段&#xff1a;化整为零学习建议第三阶段&#xff1a;实战演练实践技巧第四阶段&#xff1a;找准定位深入学习建议学习要避开的弯路自学失败的原因有很多最后&#xff1a;学习路线笔…