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 ...
随机推荐
- Postgres中表和元组的组织方式
PG version 9.5.3 PG中四种堆文件: 普通堆 临时堆 序列堆 TOAST表 PageHeaderData长度为24(截图为8.4版本,20字节)个字节包含的内容如下: 空闲空间的起始和 ...
- NGUI_Atlas
二.NGUI的图集制作: 1.概述: 将导入的图片资源全部制成一张图集,可以节约资源,当制成图集后,就可以将导入的图片资源进行删除, 再后续的操作直接使用图集中的图片即可,NGUI自带的Atlas M ...
- POJ 2367 topological_sort
Genealogical tree Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 2920 Accepted: 1962 Spe ...
- 第一次面试&第一次霸面
哈哈哈哈,第一次面试和第一次都献给了CVTE! CVTE的招聘流程有点特别:网測-- 一面--笔试--二面--offer 想起网測那天就心酸.那先在做第三部分的专业測试.计时器突然出错........ ...
- orale 查询每年、每月、每日统计量的sql语句
每年 select to_char(createtime, 'YYYY') 年, count(*) from table group by to_char(createtime, 'YYYY'); ...
- 几条jQuery代码片段助力Web开发效率提升
平滑滚动至页面顶部 以下是jQuery最为常见的一种实现效果:点击一条链接以平滑滚动至页面顶部.虽然没什么新鲜感可言,但每位开发者几乎都用得上. $("a[href='#top']" ...
- 跨平台应用集成(在ASP.NET Core MVC 应用程序中集成 Microsoft Graph)
作者:陈希章 发表于 2017年6月25日 谈一谈.NET 的跨平台 终于要写到这一篇了.跨平台的支持可以说是 Office 365 平台在设计伊始就考虑的目标.我在前面的文章已经提到过了,Micro ...
- VirtualBox 安装 Ubuntu 14.04 无法调节分辨率问题
基础环境 宿主系统:Windows 10 虚拟机系统:Ubuntu 14.04-32bit.Ubuntu 14.04-64bit VirtualBox:5.2.0 r118431 (Qt5.6.2) ...
- 获取AJAX加载的内容
1.有些网页内容使用AJAX加载,AJAX一般返回的是JSON,直接对AJAX地址进行post或get,就返回JSON数据了. 2.用抓包工具分析https://movie.douban.com/j/ ...
- Intellijidea建javaWeb以及Servlet简单实现
一.创建并设置javaweb工程1.创建javaweb工程File --> New --> Project... 点击Project后出现如下界面,选择Java Enterprise,选中 ...