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

本篇博客只是记录一下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. apply/call/bind的区别与用法

    apply 方法/call 方法 obj.call(thisObj, arg1, arg2, ...);obj.apply(thisObj, [arg1, arg2, ...]); 两者作用一致,都是 ...

  2. webrc视频数据发送处理流程详解

  3. Linux中MySQL配置文件my.cnf参数优化

    MySQL参数优化这东西不好好研究还是比较难懂的,其实不光是MySQL,大部分程序的参数优化,是很复杂的.MySQL的参数优化也不例外,对于不同的需求,还有硬件的配置,优化不可能又最优选择,只能慢慢的 ...

  4. hdu1181 变形课(vector容器+dfs)

    变形课 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others) Total Submi ...

  5. 微服务架构之RPC-client序列化细节

    通过上篇文章的介绍,知道了要实施微服务,首先要搞定RPC框架,RPC框架的职责要向[调用方]和[服务提供方]屏蔽各种复杂性: (1)让调用方感觉就像调用本地函数一样 (2)让服务提供方感觉就像实现一个 ...

  6. python 列表解析

    列表解析,主要用于动态创建列表 本篇主要说一下,lambda.map().和filter()同列表解析语句之间结合的用法 列表解析的基本语法为:[expr for iter_var in iterab ...

  7. 自学Zabbix3.8.1.1-可视化Visualisation-Graphs简单图表

    自学Zabbix3.8.1.1-可视化Visualisation-Graphs简单图表 Zabbix提供了一些简单的图表,用于可视化由项目收集的数据. 用户不需要进行配置工作来查看简单的图表.他们是由 ...

  8. 大数据学习(4)MapReduce编程Helloworld:WordCount

    Maven依赖: <dependency> <groupId>jdk.tools</groupId> <artifactId>jdk.tools< ...

  9. cookies和re

    参考:http://cuiqingcai.com/968.html   http://cuiqingcai.com/977.html

  10. 单点登录,session,jsonp(待更新)

    单点登录理解: 单点登录系统设计: ajax跨域: