ContentResolver,ContentProvider,ContentObserver使用记录
版权声明:本文出自汪磊的博客,转载请务必注明出处。
本篇博客只是记录一下ContentProvider的使用(这部分工作中用的比较少总是忘记),没有太深入研究。已经熟练掌握使用方式,想深入了解内部机制的同学可以绕过了。
一、ContentProvider概述
Android应用程序运行在不同的进程空间中,因此不同应用程序的数据是不能够直接访问的。为了增强程序之间的数据共享能力,Android系统提供了像SharedPreferences这类简单的跨越程序边界的访问方法,但这些方法都存在一定的局限性,提供数据的能力有限,安卓系统提供了另一种跨进程提供数据的方式也就ContentProvider,ContentProvider翻译过来叫做:数据提供者,是应用程序之间共享数据的一种接口机制,其他应用程序则可以在不知道数据来源的情况下,对共享数据进行增删改查等操作。在Android系统中,许多系统内置的数据也是通过ContentProvider提供给用户使用,例如通讯录、音视频图像文件等。
二、ContentProvider调用
调用者不能直接调用ContentProvider的接口函数,需要通过ContentResolver对象,通过URI间接调用ContentProvider,Android系统根据URI确定处理这个查询的ContentProvider。
三、通用资源标识符URI
URI可以理解为一个个网站的访问地址,比如百度有百度的地址,阿里有阿里的地址,同样在安卓系统中每个ContentProvider也都有自己的访问地址,ContentProvider使用的URI语法结构如下:
content://<authority>/<data_path>/<id>
- content:// 是通用前缀,表示该UIR用于ContentProvider定位资源。
- < authority > 是授权者名称,用来确定具体由哪一个ContentProvider提供资源。因此一般< authority >都由类的小写全称组成,以保证唯一性。
- < data_path > 是数据路径,用来确定请求的是哪个数据集。如果ContentProvider只提供一个数据集,数据路径则可以省略;如果ContentProvider提供多个数据集,数据路径必须指明具体数据集。数据集的数据路径可以写成多段格式,例如people/delete和people/insert。
- < id > 是数据编号,用来唯一确定数据集中的一条记录,匹配数据集中_ID字段的值。如果请求的数据不只一条,< id >可以省略。
四、创建ContentProvider
创建一个类继承ContentProvider,重载6个函数,分别为onCreate(),getType(),insert()、delete()、update()、query()。
onCreate()
一般用来初始化底层数据集和建立数据连接等工作
getType()
用来返回指定URI的MIME数据类型,若URI是单条数据,则返回的MIME数据类型以vnd.android.cursor.item开头;若URI是多条数据,则返回的MIME数据类型以vnd.android.cursor.dir/开头。
insert()、delete()、update()、query()
用于对数据集的增删改查操作。
五、UriMatcher类
UriMatcher类其实就是一个工具类,用于匹配用户传递进来的Uri。
示例:
private static final int PRESON_INSERT_CODE = 0;
private static final int PERSON_DELETE_CODE = 1;
private static final int PERSON_UPDATE_CODE = 2;
private static final int PERSON_QUERY_ALL_CODE = 3;
private static final int PERSON_QUERY_ITEM_CODE = 4;
//
private static UriMatcher uriMatcher;
private PersonSQLiteOpenHelper mOpenHelper; static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(Person.AUTHORITY, Person.PATH_INSERT,
PRESON_INSERT_CODE);
uriMatcher.addURI(Person.AUTHORITY, Person.PATH_DELETE,
PERSON_DELETE_CODE);
uriMatcher.addURI(Person.AUTHORITY, Person.PATH_UPDATE,
PERSON_UPDATE_CODE);
uriMatcher.addURI(Person.AUTHORITY, Person.PATH_QUERY_ALL,
PERSON_QUERY_ALL_CODE);
uriMatcher.addURI(Person.AUTHORITY, Person.PATH_QUERY_ITEM,
PERSON_QUERY_ITEM_CODE);
}
UriMatcher的构造函数中,UriMatcher.NO_MATCH是URI无匹配时的返回代码,值为-1。 addURI() 方法用来添加新的匹配项,语法为:
public void addURI(String authority, String path, int code)
其中authority表示匹配的授权者名称,path表示数据路径,code表示匹配成功时的返回代码。
使用示例:
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case PERSON_QUERY_ALL_CODE: // 返回多条的MIME-type
return "vnd.android.cursor.dir/person";
case PERSON_QUERY_ITEM_CODE: // 返回单条的MIME-TYPE
return "vnd.android.cursor.item/person";
default:
break;
}
return null;
}
六、ContentObserver简要介绍
ContentObserver——内容观察者,观察)特定Uri引起的数据库的变化,继而做一些相应的处理,当ContentObserver所观察的Uri发生变化时,便会触发它回调onChange方法。
ContentObserver的编写:创建一个类继承自ContentObserver,重写onChange,监听的的url数据发生变化时就会回调此方法。
示例:
public class PersonContentObserver extends ContentObserver {
//
private static final String TAG = "TestCase";
private Context mContext;
public PersonContentObserver(Handler handler,Context mContext) {
super(handler);
this.mContext = mContext;
}
@Override
public void onChange(boolean selfChange) {
//
15 }
}
ContentObserver的注册:ContentObserver的注册是由ContentResolver来完成的。
示例:
public class MainActivity extends Activity {
private PersonContentObserver mContentObserver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContentObserver = new PersonContentObserver(new Handler(),this);
getContentResolver().registerContentObserver(Person.CONTENT_URI_DELETE,
true, mContentObserver);
}
@Override
protected void onDestroy() {
//
super.onDestroy();
getContentResolver().unregisterContentObserver(mContentObserver);
}
}
void registerContentObserver(@NonNull Uri uri, boolean notifyForDescendents, @NonNull ContentObserver observer)参数说明
uri:监测的uri地址
notifyForDescendents:为true 表示可以同时匹配其派生的Uri,false只精确匹配当前Uri.
observer:就是我们自己编写的ContentObserve了。
七、Demo源码示例
1,编写ContentProvider工程,此工程演示ContentProvider的创建以及ContentObserver的使用
工程目录:

先来看看Person类:
public class Person {
public static final String AUTHORITY = "com.wanglei.personcontentprovider";
//
public static final String PATH_INSERT = "person/insert";
public static final String PATH_DELETE = "person/delete";
public static final String PATH_UPDATE = "person/update";
public static final String PATH_QUERY_ALL = "person/queryAll";
public static final String PATH_QUERY_ITEM = "person/query/#";
//
public static final Uri CONTENT_URI_INSERT = Uri.parse("content://" + AUTHORITY + "/" + PATH_INSERT);
public static final Uri CONTENT_URI_DELETE = Uri.parse("content://" + AUTHORITY + "/" + PATH_DELETE);
public static final Uri CONTENT_URI_UPDATE = Uri.parse("content://" + AUTHORITY + "/" + PATH_UPDATE);
public static final Uri CONTENT_URI_QUERY_ALL = Uri.parse("content://" + AUTHORITY + "/" + PATH_QUERY_ALL);
public static final Uri CONTENT_URI_QUERY_ITEM = Uri.parse("content://" + AUTHORITY + "/" + PATH_QUERY_ITEM);
//
public static final String KEY_ID = "_id";
public static final String KEY_NAME = "name";
public static final String KEY_AGE = "age";
}
此类只是定义了一些常量,由于只是演示,为了方便没有写成正规的javaBean.很简单,没有多余需要解释的。
接下来看下PersonSQLiteOpenHelper类,此类就是数据库的创建了,很简单,如下:
/**
* @author andong 数据库帮助类, 用于创建和管理数据库的.
*/
public class PersonSQLiteOpenHelper extends SQLiteOpenHelper { //数据库名称
private static final String DB_NAME = "person.db";
//表名称
public static final String TABLE_NAME = "person"; /**
* 数据库的构造函数
*
* @param context
*
* name 数据库名称 factory 游标工程 version 数据库的版本号 不可以小于1
*/
public PersonSQLiteOpenHelper(Context context) {
super(context, DB_NAME, null, 1);
} /**
* 数据库第一次创建时回调此方法. 初始化一些表
*/
@Override
public void onCreate(SQLiteDatabase db) { // 操作数据库
String sql = "create table " + TABLE_NAME + "(" + Person.KEY_ID
+ " integer primary key autoincrement, " + Person.KEY_NAME
+ " varchar(100), "+Person.KEY_AGE+" integer);";
db.execSQL(sql); // 创建person表
} /**
* 数据库的版本号更新时回调此方法, 更新数据库的内容(删除表, 添加表, 修改表)
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
}
接下来继续看下PersonContentProvider类:
public class PersonContentProvider extends ContentProvider {
private static final int PRESON_INSERT_CODE = 0;
private static final int PERSON_DELETE_CODE = 1;
private static final int PERSON_UPDATE_CODE = 2;
private static final int PERSON_QUERY_ALL_CODE = 3;
private static final int PERSON_QUERY_ITEM_CODE = 4;
//
private static UriMatcher uriMatcher;
private PersonSQLiteOpenHelper mOpenHelper;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(Person.AUTHORITY, Person.PATH_INSERT,
PRESON_INSERT_CODE);
uriMatcher.addURI(Person.AUTHORITY, Person.PATH_DELETE,
PERSON_DELETE_CODE);
uriMatcher.addURI(Person.AUTHORITY, Person.PATH_UPDATE,
PERSON_UPDATE_CODE);
uriMatcher.addURI(Person.AUTHORITY, Person.PATH_QUERY_ALL,
PERSON_QUERY_ALL_CODE);
uriMatcher.addURI(Person.AUTHORITY, Person.PATH_QUERY_ITEM,
PERSON_QUERY_ITEM_CODE);
}
@Override
public boolean onCreate() {
mOpenHelper = new PersonSQLiteOpenHelper(getContext());
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
switch (uriMatcher.match(uri)) {
case PERSON_QUERY_ALL_CODE: // 查询所有人的uri
if (db.isOpen()) {
Cursor cursor = db.query(PersonSQLiteOpenHelper.TABLE_NAME,
projection, selection, selectionArgs, null, null,
sortOrder);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
// db.close(); 返回cursor结果集时, 不可以关闭数据库
}
break;
case PERSON_QUERY_ITEM_CODE: // 查询的是单条数据, uri末尾出有一个id
if (db.isOpen()) {
long id = ContentUris.parseId(uri);
Cursor cursor = db.query(PersonSQLiteOpenHelper.TABLE_NAME,
projection, Person.KEY_ID + " = ?", new String[] { id
+ "" }, null, null, sortOrder);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
break;
default:
throw new IllegalArgumentException("uri不匹配: " + uri);
}
return null;
}
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case PERSON_QUERY_ALL_CODE: // 返回多条的MIME-type
return "vnd.android.cursor.dir/person";
case PERSON_QUERY_ITEM_CODE: // 返回单条的MIME-TYPE
return "vnd.android.cursor.item/person";
default:
break;
}
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
switch (uriMatcher.match(uri)) {
case PRESON_INSERT_CODE: // 添加人到person表中
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
if (db.isOpen()) {
long id = db.insert(PersonSQLiteOpenHelper.TABLE_NAME, null,
values);
db.close();
Uri newUri = ContentUris.withAppendedId(uri, id);
//通知内容观察者数据发生变化
getContext().getContentResolver().notifyChange(newUri, null);
return newUri;
}
break;
default:
throw new IllegalArgumentException("uri不匹配: " + uri);
}
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
switch (uriMatcher.match(uri)) {
case PERSON_DELETE_CODE: // 在person表中删除数据的操作
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
if (db.isOpen()) {
int count = db.delete(PersonSQLiteOpenHelper.TABLE_NAME,
selection, selectionArgs);
db.close();
//通知内容观察者数据发生变化
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
break;
default:
throw new IllegalArgumentException("uri不匹配: " + uri);
}
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
switch (uriMatcher.match(uri)) {
case PERSON_UPDATE_CODE: // 更新person表的操作
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
if (db.isOpen()) {
int count = db.update(PersonSQLiteOpenHelper.TABLE_NAME,
values, selection, selectionArgs);
db.close();
//通知内容观察者数据发生变化
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
break;
default:
throw new IllegalArgumentException("uri不匹配: " + uri);
}
return 0;
}
}
可以看到此ContentProvider内部操作对象就是person.db中的person表,并且对数据库操作完调用 getContext().getContentResolver().notifyChange(uri, null)通知对应内容观察者数据发生了变化。
PersonContentObserver类:
public class PersonContentObserver extends ContentObserver {
//
private static final String TAG = "TestCase";
private Context mContext;
public PersonContentObserver(Handler handler,Context mContext) {
super(handler);
this.mContext = mContext;
}
@Override
public void onChange(boolean selfChange) {
//
Log.i(TAG, "PersonContentObserver");
ContentResolver resolver = mContext.getContentResolver();
Cursor cursor = resolver
.query(Person.CONTENT_URI_QUERY_ALL, new String[] {
Person.KEY_ID, Person.KEY_NAME, Person.KEY_AGE }, null,
null, "_id desc");
if (cursor != null && cursor.getCount() > 0) {
int id;
String name;
int age;
while (cursor.moveToNext()) {
id = cursor.getInt(cursor.getColumnIndex(Person.KEY_ID));
name = cursor.getString(cursor.getColumnIndex(Person.KEY_NAME));
age = cursor.getInt(cursor.getColumnIndex(Person.KEY_AGE));
Log.i(TAG, "id: " + id + ", name: " + name + ", age: " + age);
}
cursor.close();
}
}
}
在我们接收到数据发生变化的时候进行的操作是重新查询person表中所有数据。
最后在MainActivity中注册PersonContentObserver:
public class MainActivity extends Activity {
private PersonContentObserver mContentObserver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContentObserver = new PersonContentObserver(new Handler(),this);
getContentResolver().registerContentObserver(Person.CONTENT_URI_DELETE,
true, mContentObserver);
}
@Override
protected void onDestroy() {
//
super.onDestroy();
getContentResolver().unregisterContentObserver(mContentObserver);
}
}
别忘了在清单文件中注册内容提供者:
<provider
android:name="com.wanglei.provider.PersonContentProvider"
android:authorities="com.wanglei.personcontentprovider"
android:exported="true" >
</provider>
接下来我们就要编写新项目测试我们的PersonContentProvider能不能正常使用以及PersonContentObserver能不能检测到数据发生发生变化了。
编写UseContentProvider项目,结构如下:

其中Person类和上面的Person类是一样的。
test.java类就是测试类了,测试增删改查:
public class test extends AndroidTestCase {
private static final String TAG = "TestCase";
public void testInsert() {
// 内容提供者访问对象
ContentResolver resolver = getContext().getContentResolver();
for (int i = 0; i < 10; i++) {
//
ContentValues values = new ContentValues();
values.put(Person.KEY_NAME, "wanglei"+i);
values.put(Person.KEY_AGE, i);
Uri uri = resolver.insert(Person.CONTENT_URI_INSERT, values);
Log.i(TAG, "uri: " + uri);
long id = ContentUris.parseId(uri);
Log.i(TAG, "添加到: " + id);
}
}
public void testDelete() {
// 内容提供者访问对象
ContentResolver resolver = getContext().getContentResolver();
String where = Person.KEY_ID + " = ?";
String[] selectionArgs = { "3" };
int count = resolver.delete(Person.CONTENT_URI_DELETE, where,
selectionArgs);
Log.i(TAG, "删除行: " + count);
}
public void testUpdate() {
// 内容提供者访问对象
ContentResolver resolver = getContext().getContentResolver();
ContentValues values = new ContentValues();
values.put(Person.KEY_NAME, "lisi");
int count = resolver.update(Person.CONTENT_URI_UPDATE, values,
Person.KEY_ID + " = ?", new String[] { "1" });
Log.i(TAG, "更新行: " + count);
}
public void testQueryAll() {
// 内容提供者访问对象
ContentResolver resolver = getContext().getContentResolver();
Cursor cursor = resolver
.query(Person.CONTENT_URI_QUERY_ALL, new String[] {
Person.KEY_ID, Person.KEY_NAME, Person.KEY_AGE }, null,
null, "_id desc");
if (cursor != null && cursor.getCount() > 0) {
int id;
String name;
int age;
while (cursor.moveToNext()) {
id = cursor.getInt(cursor.getColumnIndex(Person.KEY_ID));
name = cursor.getString(cursor.getColumnIndex(Person.KEY_NAME));
age = cursor.getInt(cursor.getColumnIndex(Person.KEY_AGE));
Log.i(TAG, "id: " + id + ", name: " + name + ", age: " + age);
}
cursor.close();
}
}
public void testQuerySingleItem() {
// 在uri的末尾添加一个id
Uri uri = ContentUris.withAppendedId(Person.CONTENT_URI_QUERY_ITEM, 1);
// 内容提供者访问对象
ContentResolver resolver = getContext().getContentResolver();
Cursor cursor = resolver.query(uri, new String[] { Person.KEY_ID,
Person.KEY_NAME, Person.KEY_AGE }, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
int id = cursor.getInt(0);
String name = cursor.getString(1);
int age = cursor.getInt(2);
cursor.close();
Log.i(TAG, "id: " + id + ", name: " + name + ", age: " + age);
}
}
}
好了,本片只是个人记录一些经常忘记的知识点方便以后忘记了可以翻翻,没有特别仔细分析。
ContentResolver,ContentProvider,ContentObserver使用记录的更多相关文章
- Android ContentProvider、ContentResolver和ContentObserver的使用
1.ContentProvider.ContentResolver和ContentObserver ContentProvider是Android的四大组件之中的一个,可见它在Android中的作用非 ...
- ContentProvider、ContentResolver、ContentObserver之间的关系
ContentProvider.ContentResolver.ContentObserver之间的关系 ContentPRrovider: * 四大组件的内容提供者,主要用于对外提供数据 * 实现各 ...
- ContentProvider 、 ContentResolver 、 ContentObserver
说说ContentProvider . ContentResolver . ContentObserver 之间的关系**a. ContentProvider 内容提供者,用于对外提供数据 b. Co ...
- AndroidContentProvider ContentResolver和ContentObserver的使用
1.ContentProvider.ContentResolver和ContentObserver ContentProvider是Android的四大组件之一,可见它在Android中 的作用非同小 ...
- ContentProvider,ContentResolver和ContentObserver 使用
1 ContentProvider内容提供者 四大组件之一,实现不同程序实现数据的共享.联系人应用就使用了ContentProvider,比如你在自己的应用可以读取和修改联系人的数据(获得相应权限). ...
- 内容观察者 ContentObserver 监听短信、通话记录数据库 挂断来电
Activity public class MainActivity extends ListActivity { private TextView tv_info; private ...
- Android应用开发学习笔记之ContentProvider
作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz ContentProvider用于为其它应用程序提供共享数据,它为不同应用程序间共享数据提供了统一的操作接口. 一. ...
- android ContentProvider 笔记
学习android的contentprovider.笔记记录于此. contentprovider作用是将数据共享给其他的应用. 参考链接 https://www.tutorialspoint.com ...
- ContentProvider要点复习
ContentProvider要点复习 ContentProvider作为四大组件之一,发挥着举足轻重的作用.与之相关联的另外两个类分别是ContentResolver和ContentObserver ...
随机推荐
- Java中的比较总结
Java中的比较问题是一个很基础又很容易混淆的问题.今天就几个容易出错的点作一个比较详细的归纳与整理,希望对大家的学习与面试有帮助. 一.==与equals()的区别 首先,我们需要知道==与equa ...
- Spring Cloud 自定义ConfigServer
公司需要将系统配置信息中的敏感信息独立存放. 现有系统采用Spring Cloud Config提供配置信息,其中敏感信息主要是Db配置,分解本次需求: (1)数据库配置信息分离(主要是Db信息). ...
- db2 update 异常
报错: -错误的sql语句:update Persons SET FirstName = 'Fred' WHERE id_P = 1com.ibm.db2.jcc.am.SqlException: O ...
- hdu 4883 区间选点
昨天比赛的时候没有做出来,本来是想用贪心的,可是贪了好久都没有招, 今天在网上搜了解题报告~好像说这是一类区间选点问题: 有一个好的做法: (1)首先把题目中的时间全转化为分钟,那么区间就在0-144 ...
- linux系统安全及应用
小伙伴们让我们一起回顾一下Linux系统安全基础知识吧 1. 系统账号清理 对于公司里刚离职或停职不久的人,处于公司信息安全考虑,给他们的账号给锁定就好了. usermod -L wangqingxi ...
- 自学Zabbix3.3-一个简单例子 添加Hosts并应用模板
Host 是 Zabbix 监控的基本载体,所有的监控项都是基于 host 的. 通过 Configuration->Hosts->Create Host 来创建监控设备 按提示填入 Na ...
- Intellij IDEA 4种配置热部署的方法
热部署可以使的修改代码后,无须重启服务器,就可以加载更改的代码. 第1种:修改服务器配置,使得IDEA窗口失去焦点时,更新类和资源 菜单Run -> EditConfiguration , 然后 ...
- 以List为例浅谈C#的学习方法
前言:关于学习方法的讨论其实是个比较模糊的概念,对于List的介绍的资料其实已经很多了,但是一般是介绍List本身,我打算分享的是,以温故List为例,来获取新知识的这么一个过程.这里的新知识也不是什 ...
- 挂载mount、卸载umount、挂载光盘U盘
mount [root@localhost ~]# mount sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime,seclabel) ...
- 添加组groupadd,修改组groupmod,删除组groupdel,将用户加入删除组gpasswd
groupadd -g GID :指定组id groupmod -g GID :修改组id -n 新组名 :修改组名 groupmod -n newname oldname groupdel grou ...