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 ...
随机推荐
- python 正则的使用 —— 编写一个简易的计算器
在 Alex 的博客上看到的对正则这一章节作业是编写一个计算器,要求能计算出下面的算式. 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 + ...
- 翻煎饼 Stacks of Flapjacks
题意:本题意为煎饼排序,大的放在上面,小的放在下面(此题输入是从上到下输入的),为煎饼排序是通过一系列的"翻转"动作来完成的.翻转动作就是将一个小铲插到一叠煎饼中的某两个煎饼之间, ...
- DXGI快速截屏录屏技术
DXGI快速截屏录屏技术 概述 很多地方都需要用到截屏/录屏技术,比如桌面直播,桌面录制等等.在微软Windows平台,有很多截屏的接口,不过大多数性能并不理想,Windows8以后微软引入了一套 ...
- Python+Selenium webdriver Api
# -*- coding: utf-8 -*- from selenium import webdriver browser = webdriver.Firefox() #browser.set_wi ...
- 运行期以索引获取tuple元素-C++14(原创)
在编译期很容易根据索引来获取对应位置的元素,因为 tuple 的帮助函数 std::get<N>(tp) 就能获取 tuple 中第 N 个元素.然而我们却不能直接在运行期通过变量来获取 ...
- 相似QQ对话框上下部分可拖动代码
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...
- 【SqlServer】【问题收集】删除同一张表中完全相同的记录
1 概述 在Sqlserver中,当通过SqlServer设计器删除同一张表中两条完全相同的记录时,会弹出如下提示: 点击“是” 弹出如下提示,不让删除 2 问题解决 这个问题很简单,用DEL ...
- 聊聊js中的typeof
内容: 1.typeof 2.值类型和引用类型 3.强制类型转换 typeof 官方文档:typeof 1.作用: 操作符返回一个字符串,指示未经计算的操作数的类型. 2.语法: typeof ope ...
- 这么说吧,java线程池的实现原理其实很简单
好处 : 线程是稀缺资源,如果被无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,合理的使用线程池对线程进行统一分配.调优和监控,有以下好处: 1.降低资源消耗: 2.提高响应速度: 3.提高线 ...
- .Net Ajax跨域请求实现
下一阵子要做一个网站Web储备一下知识,AJAX 实现跨域请求,估计会用到,以前在学 WebServer 时候老师整理的一个文档,现在便于查阅和使用现在放到我的博客中. 一般平时我写web页面的时 ...