1. 内容摘要
- ContentProvider 基本概念
- ContentProvider 的基本使用
- 操作系统短信
- 操作系统联系人
- 内容观察者ContentObserver
- 案例-短信窃听器
- 使用ContentObserver 监听自定义ContentProvider 的变化
2. ContentProvider 基本概念
内容提供者ContentProvider,是Android 的四大组件之一。内容提供者是应用程序之间共享数据的接口。
Android 系统将这种机制应用到方方面面,比如:联系人(通讯录应用程序)Provider 专为不同应用程序提供联系人数据;短信(短信应用程序)Provider 专为不同应用程序提供系统短信信息。
当应用继承ContentProvider 类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。虽然使用其他方法也可以对外共享数据,但数据访问方式会因数据存储的方式而不同,如:采用文件方式对外共享数据,需要进行文件操作读写数据;采用SharedPreferences 共享数据,需要使用SharedPreferences API 读写数据。而使用ContentProvider 共享数据的好处是统一了数据访问方式。
官方对ContentProvider 的解释如下:
Content providers manage access to a structured set of data. They encapsulate the data, and provide mechanisms for defining data security. Content providers are the standard interface that connects data in one process with code running in another process.
内容提供者管理了对结构化数据(最常见的就是数据库中数据)的访问操作。内容提供者封装了这些数据并且提供了一种安全的访问机制。内容提供者是不同进程之间交互数据(数据库数据)的标准方式。
用一张简易图演示内容提供者的工作原理,如下图所示
该图中假设手机助手App 要获取系统的短信,系统的短信是存储在数据库中的,当然该数据库只能由系统短信App 内部代码直接访问。手机助手App 直接访问短信数据库是行不通的,这时候就可以借助内容提供者,系统短信App 已经写好了内容提供者,该内容提供者对外提供了短信数据。因此手机助手App直接去访问系统短信的内容提供者即可间接实现对短信数据库的访问
3. ContentProvider 基本使用
为了演示ContentProvider 的基本用法,在该节中我们将创建两个Android 工程:01-内容提供者A(简称AppA)和内容访问者B(简称AppB)。在AppA 中创建数据库user.db,该db 中有两张表,t_woman和t_man。AppA 通过内容提供者对外提供数据库中的数据。AppB 作为一个内容访问者,通过内容解析者(ContentResolver)实现对AppA 中的数据进行增删改查功能。
3.1 在AppA 中创建MySQLiteOpenHelper 类
在该类中实现数据库和数据表的创建业务逻辑
|
|
3.2 使用测试类初始化user.db
为了让MySQLiteOpenHelper.java 中onCreate 方法被调用,我们可以采用一个测试类来实现,当然使用其他方法也是可行的。这里使用测试类初始化AppA 的数据库也是为了复习Android Junit Test的使用
|
|
在AndroidManifest.xml 中添加测试指令集和测试库。在application 节点外添加指令集
|
|
在application 节点中添加测试库
|
|
万事俱备后一定记得运行一下该测试方法,看见如下绿条和数据库文件才行哦
数据库初始化完成
3.3 在AppA 中创建内容提供者
我们已经将user.db 创建好了,接下来我们创建一个内容提供者将user.db 中的数据提供出去
|
|
3.4 在AndroidManifest.xml 中注册ContentProvider
|
|
注意:exported 属性必须设置为true 才可以被其他应用访问到该内容提供者
3.5 创建工程02-内容访问者B
在AppB 中直接使用MainActivity 类,布局文件如下
MainActivity.java 代码实现
|
|
3.6 内容提供者Uri 的书写规范
- schema,用来说明一个ContentProvider 控制这些数据。”content://“
- Authority主机名或授权:它定义了是哪个ContentProvider 提供这些数据。
- path:路径,URI 下的某一个Item。
- ID:通常定义Uri 时使用”#”号占位符代替, 使用时替换成对应的数字
- content://com.itheima.provider/person/#:#表示数据id(#代表任意数字)
- content://com.itheima.provider/person/*:*来匹配任意文本。
4. 操作系统短信
使用内容提供者操作系统短信和操作系统联系人是我们企业开发中“经常”遇到的需求,而自定义内容提供者对外提供数据反而使用的场景并不多,除非我们开发的短信或者联系人应用
4.1 准备知识
打开Android 系统源码,查看packages\providers\路径下的工程,这些就是Android 系统中的内容提供者,其中TelephonyProvider 就是短信的内容提供者文件
打开TelephonyProvider 下的src 文件,查看java 文件,其中的SmsProvider.java 即短信息内容提供者逻辑代码。UriMatcher 一般在静态代码块中进行初始化操作,查找静态代码块,找到的逻辑代码如下
|
|
通过查找系统源码,可以确定短信息内容提供者的Uri 应该为:”content://sms”。查看Android 模拟器下的/data/data/com.android.providers.telephony/databases/目录,查看其mmssms.db文件。
打开数据库,其中sms 表存储的就是短信的数据,其存储格式如下
其中,address 存储的是联系人号码,date 是发送日期,type 对应短信的类型(发送是1/接收是2),body是短信的主体内容
由于读取和插入系统短信数据库都涉及到可能侵犯用户隐私,因此创建的工程必须添加如下权限
4.2 查询系统短信
创建Android 工程03-操作系统短信。布局文件十分简单,只有两个Button,点击不同的Button 分别触发短信的读取和短信的插入功能。因此布局文件不再给出。
|
|
5. 内容提供者
- 应用的数据库是不允许其他应用访问的
- 内容提供者的作用就是让别的应用访问到你的数据库
- 自定义内容提供者,继承ContentProvider类,重写增删改查方法,在方法中写增删改查数据库的代码,举例增方法
|
|
在清单文件中定义内容提供者的标签,注意必须要有authorities属性,这是内容提供者的主机名,功能类似地址
1234<provider android:name="com.itheima.contentprovider.PersonProvider"android:authorities="com.itheima.person"android:exported="true"></provider>创建一个其他应用,访问自定义的内容提供者,实现对数据库的插入操作
|
|
6. UriMatcher
- 用于判断一条uri跟指定的多条uri中的哪条匹配
- 添加匹配规则
|
|
- 通过Uri匹配器可以实现操作不同的表
|
|
- 如果路径中带有数字,把数字提取出来的api
|
|
7. 短信数据库
- 只需要关注sms表
- 只需要关注4个字段
- body:短信内容
- address:短信的发件人或收件人号码(跟你聊天那哥们的号码)
- date:短信时间
- type:1为收到,2为发送
- 读取系统短信,首先查询源码获得短信数据库内容提供者的主机名和路径,然后
|
|
- 插入系统短信
|
|
- 插入查询系统短信需要注册权限
8. 联系人数据库
- raw_contacts表:
- contact_id:联系人id
- data表:联系人的具体信息,一个信息占一行
- data1:信息的具体内容
- raw_contact_id:联系人id,描述信息属于哪个联系人
- mimetype_id:描述信息是属于什么类型
- mimetypes表:通过mimetype_id到该表查看具体类型
8.1 读取联系人
- 先查询raw_contacts表拿到联系人id
|
|
- 然后拿着联系人id去data表查询属于该联系人的信息
|
|
- 得到data1字段的值,就是联系人的信息,通过mimetype判断是什么类型的信息
|
|
8.2 插入联系人
- 先查询raw_contacts表,确定新的联系人的id应该是多少
- 把确定的联系人id插入raw_contacts表
|
|
- 在data表插入数据
- 插3个字段:data1、mimetype、raw_contact_id1234567891011cv = new ContentValues();cv.put("data1", "赵六");cv.put("mimetype", "vnd.android.cursor.item/name");cv.put("raw_contact_id", _id);cr.insert(Uri.parse("content://com.android.contacts/data"), cv);cv = new ContentValues();cv.put("data1", "1596874");cv.put("mimetype", "vnd.android.cursor.item/phone_v2");cv.put("raw_contact_id", _id);cr.insert(Uri.parse("content://com.android.contacts/data"), cv);
- 插3个字段:data1、mimetype、raw_contact_id
9. 内容观察者
- 当数据库数据改变时,内容提供者会发出通知,在内容提供者的uri上注册一个内容观察者,就可以收到数据改变的通知
|
|
- 在内容提供者中发通知的代码
|
|
10. ContentProvider
- 四大组件之一
- 内容提供者的作用:把私有数据暴露给其他应用,通常,是把私有数据库的数据暴露给其他应用
10.1 短信数据库
- sms表
- body:短信内容
- date:短信时间
- address:对方号码
- type:发送还是接收
10.2 联系人数据库
- raw_contacts表
- contact_id:联系人id
- data表:存放联系人的详细的信息,每行数据是单独的一条联系人信息
- data1:联系人的具体的信息
- raw_contact_id:该行信息属于哪个联系人
- mimetype_id:该行信息属于什么类型
- mimetypes表:mimetype_id对应的类型的字符串
11. 内容提供者案例
案例1:自定义内容提供者
|
|
|
|
案例2:获取系统短信
|
|
案例3:插入系统短信
|
|
案例4:查询联系人
|
|
案例5:插入联系人
|
|
案例6:内容观察者
|
|
案例7:监听数据库的改变
|
|