版权声明:本文出自汪磊的博客,转载请务必注明出处。

本篇博客只是记录一下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使用记录的更多相关文章

  1. Android ContentProvider、ContentResolver和ContentObserver的使用

    1.ContentProvider.ContentResolver和ContentObserver ContentProvider是Android的四大组件之中的一个,可见它在Android中的作用非 ...

  2. ContentProvider、ContentResolver、ContentObserver之间的关系

    ContentProvider.ContentResolver.ContentObserver之间的关系 ContentPRrovider: * 四大组件的内容提供者,主要用于对外提供数据 * 实现各 ...

  3. ContentProvider 、 ContentResolver 、 ContentObserver

    说说ContentProvider . ContentResolver . ContentObserver 之间的关系**a. ContentProvider 内容提供者,用于对外提供数据 b. Co ...

  4. AndroidContentProvider ContentResolver和ContentObserver的使用

    1.ContentProvider.ContentResolver和ContentObserver ContentProvider是Android的四大组件之一,可见它在Android中 的作用非同小 ...

  5. ContentProvider,ContentResolver和ContentObserver 使用

    1 ContentProvider内容提供者 四大组件之一,实现不同程序实现数据的共享.联系人应用就使用了ContentProvider,比如你在自己的应用可以读取和修改联系人的数据(获得相应权限). ...

  6. 内容观察者 ContentObserver 监听短信、通话记录数据库 挂断来电

    Activity public class MainActivity extends ListActivity {     private TextView tv_info;     private  ...

  7. Android应用开发学习笔记之ContentProvider

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz ContentProvider用于为其它应用程序提供共享数据,它为不同应用程序间共享数据提供了统一的操作接口. 一. ...

  8. android ContentProvider 笔记

    学习android的contentprovider.笔记记录于此. contentprovider作用是将数据共享给其他的应用. 参考链接 https://www.tutorialspoint.com ...

  9. ContentProvider要点复习

    ContentProvider要点复习 ContentProvider作为四大组件之一,发挥着举足轻重的作用.与之相关联的另外两个类分别是ContentResolver和ContentObserver ...

随机推荐

  1. Java中的比较总结

    Java中的比较问题是一个很基础又很容易混淆的问题.今天就几个容易出错的点作一个比较详细的归纳与整理,希望对大家的学习与面试有帮助. 一.==与equals()的区别 首先,我们需要知道==与equa ...

  2. Spring Cloud 自定义ConfigServer

    公司需要将系统配置信息中的敏感信息独立存放. 现有系统采用Spring Cloud Config提供配置信息,其中敏感信息主要是Db配置,分解本次需求: (1)数据库配置信息分离(主要是Db信息). ...

  3. db2 update 异常

    报错: -错误的sql语句:update Persons SET FirstName = 'Fred' WHERE id_P = 1com.ibm.db2.jcc.am.SqlException: O ...

  4. hdu 4883 区间选点

    昨天比赛的时候没有做出来,本来是想用贪心的,可是贪了好久都没有招, 今天在网上搜了解题报告~好像说这是一类区间选点问题: 有一个好的做法: (1)首先把题目中的时间全转化为分钟,那么区间就在0-144 ...

  5. linux系统安全及应用

    小伙伴们让我们一起回顾一下Linux系统安全基础知识吧 1. 系统账号清理 对于公司里刚离职或停职不久的人,处于公司信息安全考虑,给他们的账号给锁定就好了. usermod -L wangqingxi ...

  6. 自学Zabbix3.3-一个简单例子 添加Hosts并应用模板

    Host 是 Zabbix 监控的基本载体,所有的监控项都是基于 host 的. 通过 Configuration->Hosts->Create Host 来创建监控设备 按提示填入 Na ...

  7. Intellij IDEA 4种配置热部署的方法

    热部署可以使的修改代码后,无须重启服务器,就可以加载更改的代码. 第1种:修改服务器配置,使得IDEA窗口失去焦点时,更新类和资源 菜单Run -> EditConfiguration , 然后 ...

  8. 以List为例浅谈C#的学习方法

    前言:关于学习方法的讨论其实是个比较模糊的概念,对于List的介绍的资料其实已经很多了,但是一般是介绍List本身,我打算分享的是,以温故List为例,来获取新知识的这么一个过程.这里的新知识也不是什 ...

  9. 挂载mount、卸载umount、挂载光盘U盘

    mount [root@localhost ~]# mount sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime,seclabel) ...

  10. 添加组groupadd,修改组groupmod,删除组groupdel,将用户加入删除组gpasswd

    groupadd -g GID :指定组id groupmod -g GID :修改组id -n 新组名 :修改组名 groupmod -n newname oldname groupdel grou ...