45. 【Android教程】内容提供者 - Content Provider

news2025/1/15 7:55:40

本节学习最后一个 Android 组件——内容提供者。顾名思义,它可以用来给其他的 App 提供各种内容,比如 Android 自带的短信、联系人、日历等等都是一个普通的 App,当你需要这些内容的时候,就可以向它们的 Content Provider 发起请求,然后拿到相应的数据。

1. 内容提供者的定义

照旧,首先看看官方解释:

Content providers are one of the primary building blocks of Android applications, providing content to applications. They encapsulate data and provide it to applications through the single ContentResolver interface. A content provider is only required if you need to share data between multiple applications. For example, the contacts data is used by multiple applications and must be stored in a content provider. If you don’t need to share data amongst multiple applications you can use a database directly via android.database.sqlite.SQLiteDatabase.
When a request is made via a ContentResolver the system inspects the authority of the given URI and passes the request to the content provider registered with the authority. The content provider can interpret the rest of the URI however it wants. The UriMatcher class is helpful for parsing URIs.

文档解释略长,这里用自己的话简要描述一发:

Content Provider 是 Android 四大组件之一,通过它可以向其他 App 提供数据。当收到其他 App 的数据请求时,会有一个 Content Resolver 接口统一对请求进行处理。数据请求通过 URI 的形式发起,每一次请求都需要带上 URI,Content Resolver 非常灵活并且可控性很强,在这里我们可以对来访者做鉴权,然后根据权限的不同给予不同敏感级别的数据,甚至可以对部分 App 拒绝提供数据。Content Provider 对数据的管理方式并不关心,可以采用 DataBase、文件、远程服务端等等 Android 支持的任何一种形式,整个工作流程如下:

2. 为什么需要内容提供者

首先我们聊聊 Content Provider 存在的意义,在 Android 中每个 App 运行于一个独立的进程,而不同进程之间一般是无法直接通信的,所以一个 App 里的数据不能直接共享给其他 App 使用,这就会让很多功能难以实现。
在 Android 系统中我们可以很简单的创建 DataBase 来管理我们的数据,但是出于安全性的考虑,DataBase 只能在 App 内部使用,App 之间是无法共享内存的。因此,为了能够方便的将自己的数据共享出去,Android 系统引入了 Content Provider 组件,让我们可以和其他的 App 很方便的进行数据交换,甚至可以用来做 IPC(进程间通信),这就是内容提供者存在的意义。

不适合的使用场景:
当你使用了内容提供者,就表示你的数据是对外暴露的,所以这是一个相对敏感的操作,很多 App 的漏洞都是由于 Content Provider 使用不当导致的,所以这里需要多加注意。
Content Provider 不仅仅可以很方便的对其他 App 提供数据,当然也可以给 App 内部其他模块、或者 App 的其他进程提供数据,在多人、多业务合作的场景下使用 Content Provider 确实很便利。但是要注意的是,如果共享的数据仅仅是一个 App 私有的数据,最好不要使用 Content Provider。

3. Content Provider 相关概念

在编写 Content Provider 之前,我们先来熟悉几个概念:

3.1 Content URI

Content URI 可以用来让 provider 唯一标识一个数据,它包括四个部分:

  • Scheme: Content Provider 的 Scheme 是固定的字符串——“content”
  • Authority: provider 的唯一标识,我们通过“Authority”字段来从众多的 Content Provider 中找到我们想要的那个。
  • Path: path 字段帮助我们描述出我们想要的最具体的数据。比如我们想要查询通话记录,可以通过不同的 path 来查询具体的“未接来电”、“播出来电”、“已接来电”等等。
  • ID: 数字类型的可选字段,在一些特殊场景下可以使用 ID 来区分不同的类别。

3.2 身份鉴权

在 Content Resolver 中使用对请求者的身份进行鉴权,通过 URI 中的 Authority 字段我们可以区分出不同的请求者。然后根据请求者的 path 字段和 ID 字段(如果有 ID)我们能够精准的知道它想要的数据,最后可以根据它的权限等级来给它提供相应的数据。

4. Content Provider 的常用操作

常用操作基本上就是下面四种,和数据库非常类似:

  1. 查询(Querying):
    查询某个 Content Provider 支持的所有数据对象
  2. 删除(Delete):
    从 Content Provider 的数据库中删除具体的数据对象
  3. 更新(Update):
    更新数据对象
  4. 插入(Insert):
    插入一个新的数据对象
  5. onCreate():
    在 provider 被创建的时候回调

5. Content Provider 使用示例

和 Broadcast 类似,Android 系统也为我们预置了很多必备的 Content Provider,我们来学习一下如何使用。

5.1 读取短信收件箱

Android 系统中短信也是一个 App,为了方便其他 App 读取短信(比如接收验证码),短信提供了一个 Content Provider 接口供我们使用,当然读取短信属于敏感操作,必须添加以下权限:

    <uses-permission android:name="android.permission.READ_SMS"/>

在 Android 6.0 以前只需要静态注册权限即可,但是这个年代应该没有多少 Android 6.0 以下的机型了。在 6.0 之后我们还需要在代码中动态申请权限,然后通过content://sms/来查询短信消息,代码如下:


package com.emercy.myapplication;

import android.Manifest;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class MainActivity extends Activity implements View.OnClickListener {

    private static final String TAG = "ContentProvider";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.get_sms).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
            int hasReadSmsPermission = checkSelfPermission(Manifest.permission.READ_SMS);
            if (hasReadSmsPermission != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(new String[]{Manifest.permission.READ_SMS}, 100);
                return;
            }
        }

        Uri uri = Uri.parse("content://sms/");
        ContentResolver resolver = getContentResolver();
        //获取的是哪些列的信息
        Cursor cursor = resolver.query(uri, new String[]{"address", "date", "type", "body"}, null, null, null);
        while (cursor.moveToNext()) {
            String address = cursor.getString(0);
            String date = cursor.getString(1);
            String type = cursor.getString(2);
            String body = cursor.getString(3);
            Log.d(TAG, "地址:" + address);
            Log.d(TAG, "时间:" + date);
            Log.d(TAG, "类型:" + type);
            Log.d(TAG, "内容:" + body);
        }
        cursor.close();
    }
}

contentView 的布局代码就不贴了,只需要一个 Button 即可。在 onClick 方法中我们首先申请权限,此时手机会弹出一个权限申请的弹窗,入剩下:

点击同意之后就可以观察 Logcat 了,过滤 Tag 为 ContentProvider,结果如下:

这样就能够顺利读取出短信相关的信息了。

5.2 读取联系人

其他的代码和读取短信一样,修改的部分就是onClick()中的部分,主要是权限申请和数据读取,修改 onClick() 中的代码如下:

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
            int hasReadSmsPermission = checkSelfPermission(Manifest.permission.READ_CONTACTS);
            if (hasReadSmsPermission != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(new String[]{Manifest.permission.READ_CONTACTS}, 100);
                return;
            }
}

ContentResolver resolver = getContentResolver();
Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
Cursor cursor = resolver.query(uri, null, null, null, null);
while (cursor.moveToNext()) {
            String cName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
            String cNum = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
            Log.d(TAG, "姓名:" + cName);
            Log.d(TAG, "号码:" + cNum);
        }
cursor.close();

然后在 AndroidManifest.xml 中加入读取联系人的权限:

<uses-permission android:name="android.permission.READ_CONTACTS"/>

第一次点击的时候同样会弹出以下权限申请弹出,授予之后即可拿到具体的联系人信息。

掌握了这两种 Content Provider 的使用,其余的像新增联系人、查询具体联系人等等其实都是换汤不换药,核心思路就是两步:1、申请权限(Android 6.0 以上需要动态申请);2、通过 URI 读取数据。

6. 小结

本节学习最后一个 Android 组件,它的功能是为其他 App 提供相关的数据,让数据可以在不同 App、不同进程之间相互共享,但是要注意的是它的适用场景,一定只是在向其他 App 输出数据的时候用,为了避免数据泄露出现安全风险,其他场景要慎用。使用 Content Provider 首先关注一下改数据是否需要申请权限,然后查询到该数据的 URI,通过拼接URI就可以完成数据的获取了。

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

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

相关文章

Siddhi 快速入门-安装Siddhi 编辑器

第一次使用Siddhi 在本节中&#xff0c;我们将使用 Siddhi 工具发行版 — Siddhi 的服务器版本&#xff0c;具有带有 GUI 的复杂的基于 Web 的编辑器&#xff08;称为“Siddhi 编辑器”&#xff09;&#xff0c;您可以在其中编写 Siddhi 应用程序并模拟事件来测试您的场景。 …

git 冲突与解决冲突

目录 1.使用 git 解决冲突 GIT 常用命令 制造冲突 解决冲突 2.使用 IDEA 解决冲突 产生冲突 解决冲突 1.使用 git 解决冲突 GIT 常用命令 命令作用git clone克隆git init初始化git add 文件名添加到暂存区git commit -m " 日志信息" 文件名提交到本地库git st…

LabVIEW连接PostgreSql

一、安装ODBC 下载对应postgreSQL版本的ODBC 下载网址&#xff1a;http://ftp.postgresql.org/pub/odbc/versions/msi/ 下载好后默认安装就行&#xff0c;这样在ODBC数据源中才能找到。 二、配置系统DSN 实现要新建好要用的数据库&#xff0c;这里的用户名&#xff1a;postg…

第十五届蓝桥杯省赛第二场C/C++B组G题【最强小队】题解

20pts 枚举所有可能的左端点、右端点&#xff0c;时间复杂度 O ( n 2 ) O(n^2) O(n2)。 对于每个区间进行遍历检测&#xff0c;时间复杂度 O ( n 3 ) O(n^3) O(n3)。 100pts 由于数据范围为 1 0 5 10^5 105&#xff0c;所以肯定只能进行一次枚举。 我们尝试枚举右端点&…

揭秘航空之心:飞机涡轮发动机3D模型震撼登场

在浩瀚的蓝天下&#xff0c;飞机如同矫健的雄鹰&#xff0c;展翅翱翔。而支撑起这雄鹰的力量之源&#xff0c;便是其心脏——涡轮发动机。今天&#xff0c;我们将通过山海鲸可视化搭建的逼真的飞机涡轮3D模型&#xff0c;揭开航空工业的神秘面纱。 飞机涡轮发动机3D模型不仅是对…

《A Discriminative Feature Learning Approach for Deep Face Recognition》阅读笔记

论文标题 《A Discriminative Feature Learning Approach for Deep Face Recognition》 一种用于深度人脸识别的判别性特征学习方法 作者 Yandong Wen、Kaipeng Zhang、Zhifeng Li 和 Yu Qiao 来自深圳市计算机视觉与专利重点实验室、中国科学院深圳先进技术研究院和香港中…

网上打印资料多少钱一张?网上打印价格是多少?

在数字化时代&#xff0c;网上打印服务正逐渐成为一种便捷、高效的打印解决方案。对于许多需要打印资料的用户来说&#xff0c;了解网上打印的价格和服务质量至关重要。那么&#xff0c;网上打印资料到底多少钱一张&#xff1f;网上打印价格又是如何呢&#xff1f;今天&#xf…

【设计模式】单例模式|最常用的设计模式

写在前面 单例模式是最常用的设计模式之一&#xff0c;虽然简单&#xff0c;但是还是有一些小坑点需要注意。本文介绍单例模式并使用go语言实现一遍单例模式。 单例模式介绍 简介 单例模式保证一个类仅有一个实例&#xff0c;并提供一个访问它的全局访问点。 使用场景&#…

超市火灾烟雾蔓延及人员疏散的matlab模拟仿真,带GUI界面

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 出口在人员的视野范围内时&#xff0c;该元胞选择朝向引导点的方向运动。出口不在人员的视野范围内时&#xff0c;作随机运动&#xff0c;8个方向的运动概率相等。…

短视频素材怎么做?视频素材库那个好?

在这个视频内容占据主导的时代&#xff0c;高质量的无水印视频素材不仅能够丰富视觉体验&#xff0c;还能显著提升你的作品吸引力。为了帮助你在广阔的创意海洋中航行&#xff0c;下面介绍的一系列视频素材网站将为你的项目注入新的活力&#xff0c;让每个创意的火花都能闪耀发…

Spring Boot集成Redisson实现延迟队列

项目场景&#xff1a; 在电商、支付等领域&#xff0c;往往会有这样的场景&#xff0c;用户下单后放弃支付了&#xff0c;那这笔订单会在指定的时间段后进行关闭操作&#xff0c;细心的你一定发现了像某宝、某东都有这样的逻辑&#xff0c;而且时间很准确&#xff0c;误差在1s内…

K-近邻算法的 sklearn 实现

实验目的与要求 掌握基于 K-近邻分类算法的编程方法通过编程理解 K-近邻分类算法和该算法的基本步骤 实验器材 硬件&#xff1a;PC 机&#xff08;参与实验的学生每人一台&#xff09;软件环境&#xff1a;Python3.7 Pycharm 实验内容 使用 sklearn 库中的 neighbors 模块实…

【java、maven环境变量配置问题】

这里写目录标题 软件版本查询所遇问题及解决方法1、java环境变量修改后不起效果&#xff1a;变量值2、java环境变量修改后不起效果&#xff1a;变量名结论&#xff1a; 软件版本查询 查询 java jdk 版本&#xff1a;java -version 查询 maven 版本&#xff1a; mvn -v 所遇问…

如何安装最新版Docker Compose?

Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。通过 Compose&#xff0c;您可以使用 YAML 文件来配置应用服务&#xff0c;然后只需一个简单的命令便能创建和启动所有服务。在本篇博客中&#xff0c;我们将详细介绍如何在 Linux 系统上安装 Docker Compos…

恭喜!喜提美国匹兹堡大学儿童医院访问学者邀请函

➡️【院校简介】 匹兹堡UPMC儿童医院该院是匹兹堡大学医学中心的一部分&#xff0c;也是大匹兹堡唯一一家专门护理26岁以下婴儿&#xff0c;儿童&#xff0c;青少年和年轻人的医院。该医院隶属于匹兹堡大学医学院&#xff0c;设有一个获得州级认证的一级儿科创伤中心&#xf…

ESP32开发WebSocket报错TRANSPORT_WS: Sec-WebSocket-Accept not found

我的芯片是ESP32-S3&#xff0c;用ESP-IDF框架进行开发的时候&#xff0c;用官方的WebSocket的example创建了项目。然后把WebSocket连接uri替换为自己的服务器后&#xff0c;运行到esp_websocket_client_start开始连接后&#xff0c;直接报错&#xff1a; E (10615) TRANSPORT…

C++|运算符重载(3)|日期类的计算

前面介绍了运算符重载相关规则和方法&#xff0c;今天用运算重载函数实现对日期类的操作。 目录 前面准备 实现功能&#xff1a; -运算符 Date类和int 相减 Date类和Date类相减 运算符 &#xff0c;-运算符 ,!运算符 >,>运算符 <,<运算符 &#xff0c;-…

MIS微调SAM模型实时交互UI界面

前言 SAM模型的基本介绍可见SAM&#xff08;Segment Anything Model&#xff09;大模型使用--point prompt_sam大模型-CSDN博客 针对Meta团队去年发布的SAM大模型在医学图像分割领域表现性能较差的情况&#xff0c;笔者收集了一些MIS领域的数据集对SAM的架构进行fine tune&am…

银河麒麟安装SSH工具

查看22端口是否启用 netstat -ntlp|grep 22 二、安装SSH工具 1、安装openssh 执行指令# sudo apt-get install openssh-server 2、更新ubuntu源 执行指令# sudo apt-get update 3、安装openssh-server 执行指令# sudo apt-get install openssh-server 4、安装ufw防火…

分享:抖音阳哥说的人力RPO项目有哪些优势?

在数字化浪潮的推动下&#xff0c;人力资源行业也迎来了前所未有的变革。抖音平台上&#xff0c;阳哥以其独到的见解和丰富的经验&#xff0c;对人力RPO(招聘流程外包)项目进行了深入解读。今天&#xff0c;我们就来探讨一下人力RPO项目究竟有哪些优势。 人力RPO项目的一大优势…