【四大组件】-- 内容提供器 ContentProvider

目录

  • 内容提供器 ContentProvider
    • 定义,是什么
    • 作用,为什么
    • 原理,机制
    • 使用方法
      • 统一资源标识符 URI
      • MIME
      • UriMatcher
      • ContentUris
      • ContentProvider
      • ContentResolver
      • 监听数据变化
    • 总结
    • 面试问题

内容提供器 ContentProvider

Android ContentProvider 基本原理和使用详解
Android ContentProvider详解

定义,是什么

四大组件之一,IPC通信的方式之一,管理 Android 以结构化方式存放的数据,以相对安全的方式封装数据(表)并且提供简易的处理机制和统一的访问接口供其他程序调用。
Android 的数据存储方式总共有五种,分别是:Shared Preferences、网络存储、文件存储、外储存储、SQLite。
有时候我们需要操作其他应用程序的一些数据,就会用到 ContentProvider。而且 Android 为常见的一些数据提供了默认的 ContentProvider(包括音频、视频、图片和通讯录等)。

作用,为什么

进程间进行数据交互 & 共享, 即跨进程通信。
主要用于在不同的应用程序之间实现数据共享的功能,提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证数据访问的安全性。

原理,机制

底层是采用Binder机制。

使用方法

要实现与其他的 ContentProvider 通信首先要查找到对应的 ContentProvider 进行匹配。
Android 中 ContenProvider 借助 ContentResolver 通过 Uri 与其他的 ContentProvider 进行匹配通信。

统一资源标识符 URI

定义:Uniform Resource Identifier , 即统一资源标识符。URI 为系统中的每一个资源赋予一个名字,比方说通话记录。每一个 ContentProvider 都拥有一个公共的 URI,用于表示 ContentProvider 所提供的数据。

作用:唯一标识 ContentProvider & 其中的数据,外界进程通过 URI 找到对应的ContentProvider & 其中的数据, 再进行数据操作。
URI分为系统预置和自定义两种,主要介绍自定义URI。
格式:[scheme:][//host:port][path][?query]
如:content://com.example.app.myprovider/tablename/1,以下对各部分进行介绍:

  1. 主题名(Schema):URI前缀,安卓CP中的URI固定为:content://,用来说明是一个Content Provider控制这些数据;

  2. URI所对应数据的唯一标识,授权信息(Authority):com.example.app.myprovider,用于唯一标识这个 ContentProvider,外部调用者可以根据这个标识来找到它。
    对于第三方应用程序,为了保证 URI 标识的唯一性,它必须是一个完整的、小写的类名。
    这个标识在provider元素的authorities属性中说明,一般是定义该 ContentProvider 的包.类的小写名称,如下:

  3. 路径(path),表名(tablename):通俗的讲就是你要操作的数据库中表的名字,或者你也可以自己定义;

  4. 记录(ID):表中的某个记录(若无指定,则返回表的全部记录)
    对于第三部分路径(path)做进一步的解释,用来表示要操作的数据,构建时应根据实际项目需求而定。如:
    操作tablename表中id为11的记录,构建路径:/tablename/11;
    操作tablename表中id为11的记录的name字段:tablename/11/name;
    操作tablename表中的所有记录:/tablename;
    操作来自文件、xml或网络等其他存储方式的数据,如要操作xml文件中tablename节点下name字段:/ tablename/name;

若需要将一个字符串转换成Uri,可以使用Uri类中的parse()方法,如:
Uri uri = URI.parse(“URI = http://www.baidu.com:8080/wenku/jiatiao.html?id=123456&name=jack”);
uri 的各个部分在安卓中都是可以通过代码获取的,下面我们就以这个 uri 为例来说下获取各个部分的方法:
uri.getScheme():获取 Uri 中的 scheme 字符串部分,在这里是 http
uri.getHost():获取 Authority 中的 Host 字符串,即 www.baidu.com
uri.getPort():获取 Authority 中的 Port 字符串,即 8080
uri.getPath():获取 Uri 中 path 部分,即 wenku/jiatiao.html
uri.getQuery():获取 Uri 中的 query 部分,即 id=15&name=du

URI中可以进行通配符匹配:
// : 匹配任意长度的任何有效字符的字符串,以下的URI 表示 匹配provider的任何内容
content://com.example.app.provider/

// # : 匹配任意长度的数字字符的字符串,以下的URI表示匹配provider中的table表的任意一行
content://com.example.app.provider/table/#

MIME

是什么:
MIME 类型一般包含两部分:类型和子类型,如:
text/html
text/css
text/xml
application/pdf
ContentProvider 会根据 URI 来返回 MIME 类型,符合MIME规范,ContentProvider 会返回一个包含两部分的字符串,第一部分标识该URI对应的数据属于多条记录(集合记录,dir)还是单条记录(item),第二部分是自定义类型,如下:
集合记录(dir):vnd.android.cursor.dir/自定义
单条记录(item):vnd.android.cursor.item/自定义
vnd 表示这些类型和子类型具有非标准的、供应商特定的形式。Android中类型已经固定好了,不能更改,只能区别是集合还是单条具体记录,子类型可以按照格式自己填写。

UriMatcher

Uri 代表要操作的数据,在对数据进行获取时需要在CP中对Uri进行解析和匹配,来判断访问者要访问的数据是否是在本CP中提供。
Android 提供了两个用于操作 Uri 的工具类,分别为 UriMatcher 和 ContentUris 。

  1. UriMatcher 类用于为CP添加Uri匹配,并在CP的被调用接口中针对入参Uri进行匹配。
    添加Uri匹配代码如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
UriMatcher  sMatcher = new UriMatcher(UriMatcher.NO_MATCH);// 针对欲访问tablename表所有数据的Uri,匹配成功,并返回匹配码为1
sMatcher.addURI("content://com.wang.provider.myprovider", " tablename ", 1);// 针对欲访问tablename表某一行单条数据的Uri,匹配成功,并返回匹配码为2
sMatcher.addURI("com.wang.provider.myprovider", "tablename/#", 2);

此处采用 addURI 注册了两个需要用到的 URI;注意,添加第二个 URI 时,路径后面的 id 采用了通配符形式 “#”,表示 URI对应访问表的某一行单条数据。
注册完需要匹配的 Uri 后,可以使用 sMatcher.match(Uri) 方法对输入的 Uri 进行匹配,如果匹配就返回对应的匹配码,匹配码为调用 addURI() 方法时传入的第三个参数。

switch (sMatcher.match(Uri.parse("content://com.zhang.provider.yourprovider/tablename/100"))) {case 1://match 1, todo somethingbreak;case 2: // 匹配成功//match 2, todo somethingbreak;default://match nothing, todo somethingbreak;
}

ContentUris

ContentUris 类用于操作 Uri 路径后面的 ID 部分,它有两个比较实用的方法:withAppendedId(Uri uri, long id) 和 parseId(Uri uri)。

  1. withAppendedId(Uri uri, long id) 用于为路径加上 ID 部分
Uri uri = Uri.parse("content://cn.scu.myprovider/user")//生成后的Uri为:content://cn.scu.myprovider/user/7
Uri resultUri = ContentUris.withAppendedId(uri, 7); 
  1. parseId(Uri uri) 则从路径中获取 ID 部分
Uri uri = Uri.parse("content://cn.scu.myprovider/user/7")//获取的结果为:7
long personid = ContentUris.parseId(uri);

ContentProvider

分为系统CP和自定义CP

  1. 系统CP
    如何获取:通过系统CP对应的唯一的URI获取,注意添加对应的访问权限,使用完成后记得及时关闭cursor。
  2. 自定义CP
    (1). 需要在AM文件中注册:
    (2). 需要继承ContentProvider 抽象类,实现对应的方法,如下:
    public boolean onCreate():在创建 ContentProvider 时使用,完成数据库的创建、连接、文件加载等资源准备操作。
    初始化CP时才会被调用,用于创建和升级db,返回true表示初始化成功,false表示失败。只有当存在ContentResolver尝试访问我们程序中的数据时,CP才会被初始化。

public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):用于查询指定 uri 的数据返回一个 Cursor
public Uri insert(Uri uri, ContentValues values):用于向指定uri的 ContentProvider 中添加数据
public int delete(Uri uri, String selection, String[] selectionArgs):用于删除指定 uri 的数据,返回被删除的行数
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):用户更新指定 uri 的数据,返回被更新内容的行数
数据访问的方法 insert,delete 和 update 可能被多个线程同时调用,此时必须是线程安全的。

public String getType(Uri uri):用于返回指定的 Uri 对象所对应的表数据 MIME 类型。
一个URI所对应的数据的MIME类型主要由3部分组成,Android对这3个部分做了如下格式规定:

  1. 必须以vnd开头;
  2. 如果对应的数据属于集合类型,则后接android.cursor.dir/,如果属于单条数据,则后接android.cursor.item/;
  3. 最后接上vnd..
    则对于content://com.example.app.provider/table/1这个uri,其对应的MIME类型就应该写成:android.cursor.item/vnd.com.example.app.provider.table1;

ContentResolver

获取:ContentResolver resolver = getContentResolver();
构造Uri:Uri uri = Uri.parse(“content://com.wang.provider.myprovider/tablename”);

添加记录:
// 添加一条记录
ContentValues values = new ContentValues();
values.put(“name”, “wang1”);
values.put(“age”, 28);
resolver.insert(uri, values);

更新记录:
// 把id为1的记录的name字段值更改新为zhang1
ContentValues updateValues = new ContentValues();
updateValues.put(“name”, “zhang1”);
Uri updateIdUri = ContentUris.withAppendedId(uri, 1);
resolver.update(updateIdUri, updateValues, null, null);

删除记录:
// 删除id为2的记录
Uri deleteIdUri = ContentUris.withAppendedId(uri, 2);
resolver.delete(deleteIdUri, null, null);

监听数据变化

如果ContentProvider的访问者需要知道数据发生的变化,可以在ContentProvider发生数据变化时调用getContentResolver().notifyChange(uri, null)来通知注册在此URI上的访问者。只给出类中监听部分的代码:

public class MyProvider extends ContentProvider {public Uri insert(Uri uri, ContentValues values) {db.insert("tablename", "tablenameid", values);getContext().getContentResolver().notifyChange(uri, null);}
}

而访问者必须使用 ContentObserver 对数据(数据采用 uri 描述)进行监听,当监听到数据变化通知时,系统就会调用 ContentObserver 的 onChange() 方法:

getContentResolver().registerContentObserver(Uri.parse("content://com.ljq.providers.personprovider/person"),true, new PersonObserver(new Handler()));
public class PersonObserver extends ContentObserver{public PersonObserver(Handler handler) {super(handler);}public void onChange(boolean selfChange) {//to do something}
}

总结

使用CP实现跨进程数据访问的总流程:
内容提供者进程:

  1. 创建MyContentPorvider,继承ContentProvider父类
  2. 在MyContentProvider类中,通过UriMatcher为CP添加Uri匹配
  3. 重写MyContentProvider的相关方法
  4. 在AM中声明provider,并在标签中声明authority,必要时还应该有对应访问权限的声明用于保护
    内容访问者进程:
  5. 获取ContentResolver:ContentResolver resolver = context.getContentResolver();
  6. 构建并解析欲访问内容对应的Uri
  7. 通过调用ContentResolver相关接口进行内容访问

面试问题

什么是ContentProvider及其使用
ContentProvider的权限管理
ContentProvider,ContentResolver,ContentObserver之间的关系
ContentProvider的实现原理
ContentProvider的优点
Uri是什么

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

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

相关文章

【spring】第二篇 bean实例化

对象已经能交给Spring的IOC容器来创建了,但是容器是如何来创建对象的呢? 就需要研究下bean的实例化过程,在这块内容中主要解决两部分内容,分别是 bean是如何创建的 实例化bean的三种方式,构造方法,静态工厂和实例工厂 在讲解这…

Linux C/C++时间操作

C11提供了操作时间的库chrono库,从语言级别提供了支持chrono库屏蔽了时间操作的很多细节,简化了时间操作 Unix操作系统根据计算机产生的年代把1970年1月1日作为UNIX的纪元时间,1970年1月1日是时间的中间点,将从1970年1月1日起经过…

WPF -> MVVM

1.1安装MVV MLight 打开 Visual Studio 2022。 在顶部菜单栏中选择“工具” -> “NuGet 包管理器” -> “程序包管理器控制台”。 在控制台中输入以下命令,并按回车键运行: Install-Package MvvmLightLibsStd104.等待安装完成后,你就…

十_信号13 - abort()

abort() 1 首先进程不能忽略 SIGABRT信号 2 要么在 SIGABRT信号的处理函数中 清理缓冲区并自己退出进程。如果信号处理函数中没有执行退出进程操作,返回到 abort()函数中,要求在 abort()函数中结束进程,不能返回到其调用者

数据库(17)——DCL数据控制语言

DCL DCL是Data Control Language数据控制语言,用来管理数据库用户、控制数据库的访问权限。 DCL-管理用户 语法 1.查询用户 USE mysql; SELECT * FROM user; 也可以直接在datagrip找到user表 我们要操作用户要通过User和Host同时定位。Host表示当前用户只能在哪个…

音视频开发9 FFmpeg 解复用相关整体说明,重要API说明

一,播放器框架 二 常用音视频术语 容器/文件(Conainer/File): 即特定格式的多媒体文件, 比如mp4、flv、mkv等。 媒体流(Stream): 表示时间轴上的一段连续数据&#xff0…

SIMBA:单细胞嵌入与特征

目前大多数单细胞分析管道仅限于细胞嵌入,并且严重依赖于聚类,而缺乏显式建模不同特征类型之间相互作用的能力。此外,这些方法适合于特定的任务,因为不同的单细胞问题的表述方式不同。为了解决这些缺点,SIMBA作为一种图…

050、Python 随机生成数据的方法

要生成随机数据,我们可以使用 random 模块。random 模块提供了多种函数来生成随机数、随机选择元素等。 以下是一些常用的方法: 1、生成随机整数: random.randint(a, b): 生成一个在区间 [a, b] 内的随机整数。random.randrange(start, st…

【Python网络爬虫】详解python爬虫中URL资源抓取

🔗 运行环境:PYTHON 🚩 撰写作者:左手の明天 🥇 精选专栏:《python》 🔥 推荐专栏:《算法研究》 #### 防伪水印——左手の明天 #### 💗 大家好🤗&#x1f91…

Java中的JDBC如何连接数据库并执行操作

JDBC(Java Database Connectivity)是Java编程语言中用来连接和操作数据库的一组API。以下是一个基本的步骤指南,用于连接数据库并执行操作: 导入JDBC驱动 首先,你需要将数据库的JDBC驱动添加到你的项目依赖中。如果你…

bootstrap5-学习笔记2-模态框+弹窗+tooltip+popover+信息提示框

参考: Bootstrap5 教程 | 菜鸟教程 https://www.runoob.com/bootstrap5/bootstrap5-tutorial.html Bootstrap 入门 Bootstrap v5 中文文档 v5.3 | Bootstrap 中文网 https://v5.bootcss.com/docs/getting-started/introduction/ 之前用bootstrap2和3比较多&#x…

Element-UI 的快速入门指南

Element-UI 是一款为开发者提供丰富组件和功能的 Vue.js 2.0 基于框架的桌面端 UI 组件库。以下是 Element-UI 的快速入门指南。 1. 安装 Element-UI 使用 npm 安装 首先,确保你已经安装了 Node.js 和 npm。然后在你的项目目录下运行以下命令来安装 Element-UI&a…

上位机图像处理和嵌入式模块部署(f407 mcu中的单独烧录方法)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 前面我们说过,stm32有三种烧录方法,一种是st-link v2,一种是dap,一种是j-link。不过我们在实际操作…

架构设计之可部署和可管理属性深度剖析:从理论到实践的完美融合

文章目录 引言一、可部署属性的深度剖析1.1 理论基础1.2 实践应用1.3 关键要素1.4 评估方法 二、可管理属性的深度剖析2.1 理论基础2.2 实践应用2.3 关键要素2.4 评估方法 三、从理论到实践的完美融合3.1. 架构设计阶段的融合3.2. 开发过程中的融合3.3. 部署和管理的融合3.4. 监…

54. 螺旋矩阵【rust题解】

题目 给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。 示例 示例 1 输入:matrix [[1,2,3],[4,5,6],[7,8,9]] 输出:[1,2,3,6,9,8,7,4,5] 示例 2 输入:matrix [[1,2,3,4],[5,6,…

Leecode---栈---每日温度 / 最小栈及栈和队列的相互实现

栈:先入后出;队列:先入先出 一、每日温度 Leecode—739题目: 给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温…

【安卓跨进程通信IPC】-- Binder

目录 BinderBinder是什么?进程空间分配进程隔离Binder跨进程通信机制模型优点AIDL常见面试题 Binder 夯实基础之超详解Android Binder的工作方式与原理以及aidl示例代码 比较详细的介绍:Android跨进程通信:图文详解 Binder机制 原理 操作系统…

可变引用useRef()保存可变数据

场景:useState的状态值一旦通过状态更新函数改变了,React 就会重新渲染组件。 方法:可变引用useRef()保存可变数据,这些数据在更改时不会触发重新渲染 每次渲染组件时,countRenderRef可变引用的值都会使countRenderRef…

Cargo字节镜像源

在 Windows 系统默认为:%USERPROFILE%.cargo,在类 Unix 系统默认为:$HOME/.cargo,在此目录下新建config文件,填写内容如下: [source.crates-io] replace-with rsproxy[source.rsproxy] registry "ht…

GPT-4o横空出世

GPT-4o的技术革新和优化显著,尤其是其多模态处理、速度与性能优化、以及情感理解与表达能力方面。而个人整体感受则普遍正面积极。 GPT-4o的版本对比: 技术进化:从GPT-3到GPT-4再到GPT-4o,OpenAI的模型在参数数量、训练方法、以及…