1、ContentProvider、ContentResolver和ContentObserver

ContentProvider是Android的四大组件之中的一个,可见它在Android中的作用非同小可。它基本的作用是:实现各个应用程序之间的(跨应用)数据共享。比方联系人应用中就使用了ContentProvider,你在自己的应用中能够读取和改动联系人的数据,只是须要获得对应的权限。事实上它也仅仅是一个中间人,真正的数据源是文件或者SQLite等。

一个应用实现ContentProvider来提供内容给别的应用来操作。 通过ContentResolver来操作别的应用数据,当然在自己的应用中也能够。

ContentObserver——内容观察者。目的是观察(捕捉)特定Uri引起的数据库的变化。继而做一些对应的处理。它类似于数据库技术中的触发器(Trigger),当ContentObserver所观察的Uri发生变化时,便会触发它。触发器分为表触发器、行触发器。对应地ContentObserver也分为“表“ContentObserver、“行”ContentObserver。当然这是与它所监听的Uri MIME Type有关的。

2、Contacts Demo

1)、基本功能实现

接下来通过一个简单的存储联系人信息的demo,来学习怎么创建自己定义的ContentProvider。这里数据源选用SQLite。最经常使用的也是这个。

(1) 创建一个类NoteContentProvider。继承ContentProvider。须要实现以下5个方法:
query
insert
update
delete
getType

public class ContactsContentProvider extends ContentProvider{@Overridepublic boolean onCreate() {// TODO Auto-generated method stubreturn false;}@Overridepublic int delete(Uri arg0, String arg1, String[] arg2) {// TODO Auto-generated method stubreturn 0;}@Overridepublic String getType(Uri arg0) {// TODO Auto-generated method stubreturn null;}@Overridepublic Uri insert(Uri arg0, ContentValues arg1) {// TODO Auto-generated method stubreturn null;}@Overridepublic Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3,String arg4) {// TODO Auto-generated method stubreturn null;}@Overridepublic int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {// TODO Auto-generated method stubreturn 0;}}

(2)先来设计一个数据库,用来联系人信息,主要包括_ID,name,telephone,create_date,content五个字段。group_name字段等后面升级部分再做使用。

创建ProviderMetaData类,封装URI和数据库、表、字段相关信息,源代码例如以下:

public class ProviderMetaData {public static final String AUTHORITY = "com.johnny.contactsprovider";public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);public static final class ContactsData implements BaseColumns{public static final String TABLE_NAME = "contacts";public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, TABLE_NAME);public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact";public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact";public static final String CONTACT_NAME = "name";public static final String CONTACT_TELEPHONE = "telephone";public static final String CONTACT_CREATE_DATE = "create_date";public static final String CONTACT_CONTENT = "content";public static final String CONTACT_GROUP = "group_name";public static final String DEFAULT_ORDERBY = "create_date DESC";public static final String SQL_CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " ("+ _ID + " INTEGER PRIMARY KEY,"+ CONTACT_NAME + " VARCHAR(50),"+ CONTACT_TELEPHONE + " VARCHAR(11),"+ CONTACT_CONTENT +" TEXT,"+ CONTACT_CREATE_DATE + " INTEGER"+ ");" ;}
}

AUTHORITY代表授权,该字符串和在Android描写叙述文件AndroidManifest.xml中注冊该ContentProvider时的android:authorities值一样,ContactsData继承BaseColumns,后者提供了标准的_id字段。表示行ID。
熟悉Content Provider(内容提供者)的应该知道。我们能够通过UriMatcher类注冊不同类型的Uri,我们能够通过这些不同的Uri来查询不同的结果。

依据Uri返回的结果。Uri Type能够分为:返回多条数据的Uri、返回单条数据的Uri。
Android遵循类似的约定来定义MIME类型,每一个内容类型的Android MIME类型有两种形式:多条记录(集合)和单条记录。
多条记录
vnd.android.cursor.dir/contact
单条记录
vnd.android.cursor.item/contact
vnd表示这些类型和子类型具有非标准的、供应商特定的形式。Android中类型已经固定好了。不能更改。仅仅能差别是集合还是单条详细记录,子类型/之后的内容能够依照格式随便填写。

在使用Intent时。会用到MIME这玩意。依据Mimetype打开符合条件的活动。

(3) ContentProvider是依据URI来获取数据的。那它怎么区分不同的URI呢。由于不管是获取笔记列表还是获取一条笔记都是调用query方法,如今来实现这个功能。须要用到类UriMatcher,该类能够帮助我们识别URI类型。以下看实现源代码:

static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);static final HashMap<String, String> CONTACTS_PROJECTION_MAP = new HashMap<String, String>();private static final int CONTACTS = 1;private static final int CONTACTS_ID = 2;static{final UriMatcher matcher = URI_MATCHER;matcher.addURI(ProviderMetaData.AUTHORITY, "contacts", CONTACTS);matcher.addURI(ProviderMetaData.AUTHORITY, "contacts/#", CONTACTS_ID);HashMap<String, String> map = CONTACTS_PROJECTION_MAP;map.put(ContactsData._ID, ContactsData._ID);map.put(ContactsData.CONTACT_NAME, ContactsData.CONTACT_NAME);map.put(ContactsData.CONTACT_TELEPHONE, ContactsData.CONTACT_TELEPHONE);map.put(ContactsData.CONTACT_CONTENT, ContactsData.CONTACT_CONTENT);map.put(ContactsData.CONTACT_CREATE_DATE, ContactsData.CONTACT_CREATE_DATE);}

这段代码是NoteContentProvider类中的。UriMatcher的工作原理:首先须要在UriMatcher中注冊URI模式。每个模式跟一个唯一的编号关联,注冊之后,在使用中就能够依据URI得到相应的编号。当模式不匹配时,UriMatcher将返回一个NO_MATCH常量,这样就能够区分了。

(4) 还需为查询设置一个投影映射,主要是将抽象字段映射到数据库中真实的字段名称,由于这些字段有时是不同的名称。既抽象字段的值能够不跟数据库中的字段名称一样。

这里使用HashMap来完毕,key是抽象字段名称,value相应数据库中的字段名称,只是这里我把两者的值设置是一样的,在NoteContentProvider.java中加入如上面所看到的的代码。

(5) 在NoteContentProvider.java中创建一个内部类DatabaseHelper。继承自SQLiteOpenHelper,完毕数据库表的创建、更新,这样能够通过它获得数据库对象,相关代码例如以下。

private class DatabaseHelper extends SQLiteOpenHelper{static final String DATABASE_NAME = "test.db";static final int DATABASE_VERSION = 1;public DatabaseHelper(Context context) {super(context, DATABASE_NAME, null, DATABASE_VERSION);// TODO Auto-generated constructor stub}@Overridepublic void onCreate(SQLiteDatabase db) {// TODO Auto-generated method stubdb.execSQL(ContactsData.SQL_CREATE_TABLE);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {// TODO Auto-generated method stubonCreate(db);}}

(6) 如今来分别实现第一步中未实现的5个方法,先来实现query方法。这里借助SQLiteQueryBuilder来为查询设置投影映射以及设置相关查询条件。看源代码实现:

    @Overridepublic Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder) {// TODO Auto-generated method stubSQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();switch(URI_MATCHER.match(uri)){case CONTACTS_ID:queryBuilder.setTables(ContactsData.TABLE_NAME);queryBuilder.setProjectionMap(CONTACTS_PROJECTION_MAP);queryBuilder.appendWhere(ContactsData.TABLE_NAME + "._id="+Long.toString(ContentUris.parseId(uri)));break;case CONTACTS:queryBuilder.setTables(ContactsData.TABLE_NAME);queryBuilder.setProjectionMap(CONTACTS_PROJECTION_MAP);break;}String orderBy;if(TextUtils.isEmpty(sortOrder)){orderBy = ContactsData.DEFAULT_ORDERBY;} else {orderBy = sortOrder;}SQLiteDatabase db = mDbHelper.getReadableDatabase();Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, orderBy);return cursor;}

返回的是一个Cursor对象,它是一个行集合,包括0和多个记录,类似于JDBC中的ResultSet,能够前后移动游标。得到每行每列中的数据。注意的是。使用它须要调用moveToFirst(),由于游标默认是在第一行之前。
(7)实现insert方法,实现把记录插入到基础数据库中。然后返回新创建的记录的URI。

    @Overridepublic Uri insert(Uri uri, ContentValues values) {// TODO Auto-generated method stubSQLiteDatabase db = mDbHelper.getWritableDatabase();long id = db.insertOrThrow(ContactsData.TABLE_NAME, null, values);// 更新数据时,通知其它ContentObservergetContext().getContentResolver().notifyChange(ContactsData.CONTENT_URI, null);if(id > 0){return ContentUris.withAppendedId(uri, id);}return null;}

(8) 实现update方法,依据传入的列值和where字句来更新记录,返回更新的记录数,看源代码:

  @Overridepublic int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {// TODO Auto-generated method stubSQLiteDatabase db = mDbHelper.getWritableDatabase();int modified = 0;switch(URI_MATCHER.match(uri)){case CONTACTS_ID:selection = DatabaseUtils.concatenateWhere(selection,ContactsData.TABLE_NAME + "._id=?");selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs,new String[]{Long.toString(ContentUris.parseId(uri))});Log.d("Test", "selectionArgs 0"+selectionArgs);modified = db.update(ContactsData.TABLE_NAME, values, selection, selectionArgs);break;case CONTACTS:modified = db.update(ContactsData.TABLE_NAME, values, selection, selectionArgs);Log.d("Test", "selectionArgs 1"+selectionArgs);break;}// 更新数据时,通知其它ContentObservergetContext().getContentResolver().notifyChange(ContactsData.CONTENT_URI, null);return modified;}

notifyChange函数是在更新数据时,通知其它监听对象。
(9)实现delete方法,该方法返回删除的记录数。

   @Overridepublic int delete(Uri uri, String selection, String[] selectionArgs) {// TODO Auto-generated method stubSQLiteDatabase db = mDbHelper.getWritableDatabase();int deleted = 0;switch(URI_MATCHER.match(uri)){case CONTACTS_ID:selection = DatabaseUtils.concatenateWhere(selection,ContactsData.TABLE_NAME + "._id=?

"); selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, new String[]{Long.toString(ContentUris.parseId(uri))}); Log.d("Test", "selectionArgs 0"+selectionArgs); deleted = db.delete(ContactsData.TABLE_NAME, selection, selectionArgs); break; case CONTACTS: deleted = db.delete(ContactsData.TABLE_NAME, selection, selectionArgs); Log.d("Test", "selectionArgs 1"+selectionArgs); break; } // 更新数据时,通知其它ContentObserver getContext().getContentResolver().notifyChange(ContactsData.CONTENT_URI, null); return deleted; }

(10) 实现getType方法,依据URI返回MIME类型。这里主要用来区分URI是获取集合还是单条记录。这种方法在这里临时没啥用处,在使用Intent时实用。

  @Overridepublic String getType(Uri uri) {// TODO Auto-generated method stubswitch(URI_MATCHER.match(uri)){case CONTACTS:return ContactsData.CONTENT_TYPE;case CONTACTS_ID:return ContactsData.CONTENT_ITEM_TYPE;
//        default:
//            throw new IllegalArgumentException("Unknow URI: " + uri);}return null;}

(11) 在AndroidManifest.xml中注冊该ContentProvider,这样系统才找得到,当然你也能够设置相关的权限。这里就不设置了

      <providerandroid:name="com.johnny.testcontentprovider.ContactsContentProvider"android:authorities="com.johnny.contactsprovider"></provider>

(12)到如今为止。自己定义ContentProvider的所有代码已经完毕。以下创建一个简单的应用来測试一下。
主要測试insert、update、delete、query这四个函数。

    private void insertContact1(){ContentValues values = new ContentValues();values.put(ContactsData.CONTACT_NAME, "James");values.put(ContactsData.CONTACT_TELEPHONE, "18888888888");values.put(ContactsData.CONTACT_CONTENT, "NBA Star");values.put(ContactsData.CONTACT_CREATE_DATE, System.currentTimeMillis());Uri uri = getContentResolver().insert(ContactsData.CONTENT_URI, values);Log.d("Test", "uri = "+uri);}private void deleteContact1(){int count = getContentResolver().delete(ContactsData.CONTENT_URI, ContactsData.CONTACT_NAME+"='James'", null);Log.d("Test", "count = "+count);}private void updateContact1(){ContentValues values = new ContentValues();values.put(ContactsData.CONTACT_TELEPHONE, "16666666666");int count = getContentResolver().update(ContactsData.CONTENT_URI,values, ContactsData.CONTACT_NAME+"='James'", null);Log.d("Test", "count = "+count);}private void updateContact1ID(){ContentValues values = new ContentValues();values.put(ContactsData.CONTACT_TELEPHONE, "17777777");int count = getContentResolver().update(ContentUris.withAppendedId(ContactsData.CONTENT_URI,2),values, null, null);Log.d("Test", "count = "+count);}private void queryContact1(){//Cursor cursor = this.getContentResolver().query(ContactsData.CONTENT_URI, null, ContactsData.CONTACT_NAME+"='James'", null, null);Cursor cursor = this.getContentResolver().query(ContactsData.CONTENT_URI, null, ContactsData.CONTACT_NAME+"=?

", new String [] {"James"}, null); Log.e("test ", "count=" + cursor.getCount()); cursor.moveToFirst(); while(!cursor.isAfterLast()) { String name = cursor.getString(cursor.getColumnIndex(ContactsData.CONTACT_NAME)); String telephone = cursor.getString(cursor.getColumnIndex(ContactsData.CONTACT_TELEPHONE)); long createDate = cursor.getLong(cursor.getColumnIndex(ContactsData.CONTACT_CREATE_DATE)); Log.e("Test", "name: " + name); Log.e("Test", "telephone: " + telephone); Log.e("Test", "date: " + createDate); cursor.moveToNext(); } cursor.close(); }

在插入两个数据后分别运行updateContact1()和updateContact1ID()函数。你会发现他们两个分别运行了ContactsContentProvider.update的两个不同地方:分别为case CONTACTS:和case CONTACTS_ID:,如今能够理解

        matcher.addURI(ProviderMetaData.AUTHORITY, "contacts", CONTACTS);matcher.addURI(ProviderMetaData.AUTHORITY, "contacts/#", CONTACTS_ID);

的不同含义了吧?

(13)创建数据库监听器ContentObserver

在MainActivity中增加下面代码:

    private ContentObserver mContentObserver = new ContentObserver(new Handler()) {@Overridepublic void onChange(boolean selfChange) {// TODO Auto-generated method stubLog.d("Test", "mContentObserver onChange");super.onChange(selfChange);}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);if (savedInstanceState == null) {getSupportFragmentManager().beginTransaction().add(R.id.container, new PlaceholderFragment()).commit();}getContentResolver().registerContentObserver(ContactsData.CONTENT_URI, true, mContentObserver);}

每次通过insert、delete、update改变数据库内容时。都会调用ContentObserver的onChange方法,因此。能够在这种方法内做出针对数据库变化的反应。比方更新UI等。

2)、数据库的升级

当应用公布一段时间之后,我们须要改变数据库的结构,那么就须要对数据库的升级了:
将DatabaseHelper类中的DATABASE_VERSION设置为2,而且在onUpgrade函数中实现升级的代码:

   private class DatabaseHelper extends SQLiteOpenHelper{static final String DATABASE_NAME = "test.db";static final int DATABASE_VERSION = 2;public DatabaseHelper(Context context) {super(context, DATABASE_NAME, null, DATABASE_VERSION);// TODO Auto-generated constructor stub}@Overridepublic void onCreate(SQLiteDatabase db) {// TODO Auto-generated method stubdb.execSQL(ContactsData.SQL_CREATE_TABLE);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {// TODO Auto-generated method stubLog.d("Test", "onUpgrade oldVersion = "+oldVersion+", newVersion = "+newVersion);//onCreate(db);for(int i = oldVersion+1;i <= newVersion;i++){switch(i){case 2:db.execSQL("ALTER TABLE " + ContactsData.TABLE_NAME + " ADD COLUMN " + ContactsData.CONTACT_GROUP + " TEXT");break;}}}}

也要记得要对onCreate做对应的改动,是用户清除数据时又一次建表是也会生效

        public static final String SQL_CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " ("+ _ID + " INTEGER PRIMARY KEY,"+ CONTACT_NAME + " VARCHAR(50),"+ CONTACT_TELEPHONE + " VARCHAR(11),"+ CONTACT_CONTENT +" TEXT,"+ CONTACT_CREATE_DATE + " INTEGER,"+ CONTACT_GROUP + " TEXT"+ ");" ;

以下是升级前后数据库的结果:

用以下代码为DATABASE_VERSION = 2的数据库中的James设在组别和增加Howard联系人:

    private void modifyContact1(){ContentValues values = new ContentValues();values.put(ContactsData.CONTACT_GROUP, "Miami");int count = getContentResolver().update(ContactsData.CONTENT_URI,values, ContactsData.CONTACT_NAME+"='James'", null);Log.d("Test", "count = "+count);}private void insertContact2(){ContentValues values = new ContentValues();values.put(ContactsData.CONTACT_NAME, "Howard");values.put(ContactsData.CONTACT_TELEPHONE, "13333333333");values.put(ContactsData.CONTACT_CONTENT, "NBA Star");values.put(ContactsData.CONTACT_GROUP, "Rockets");values.put(ContactsData.CONTACT_CREATE_DATE, System.currentTimeMillis());Uri uri = getContentResolver().insert(ContactsData.CONTENT_URI, values);Log.d("Test", "uri = "+uri);}private void queryContacts1(){Cursor cursor = this.getContentResolver().query(ContactsData.CONTENT_URI, null, ContactsData.CONTACT_GROUP+" IN ('Miami','Rockets')", null, null);Log.e("Test ", "count=" + cursor.getCount());cursor.moveToFirst();while(!cursor.isAfterLast()) {String name = cursor.getString(cursor.getColumnIndex(ContactsData.CONTACT_NAME));String telephone = cursor.getString(cursor.getColumnIndex(ContactsData.CONTACT_TELEPHONE));long createDate = cursor.getLong(cursor.getColumnIndex(ContactsData.CONTACT_CREATE_DATE));Log.e("Test", "name: " + name);Log.e("Test", "telephone: " + telephone);Log.e("Test", "date: " + createDate);cursor.moveToNext();}cursor.close();}

结果例如以下:

參考:http://codingnow.cn/android/1078.html

转载于:https://www.cnblogs.com/wzzkaifa/p/6731611.html

Android ContentProvider、ContentResolver和ContentObserver的使用相关推荐

  1. android的contentResolver,contentProvider简介(附带个获取手机联系人及头像实例)

    介绍 android中有个ContentProvider(内容提供者)还有个ContentResolver(内容解析者) 简单来说,contentProvider就是将自己app的数据库共享,使得其他 ...

  2. Android ContentProvider和ContentResolver实例

    ContentResolver和ContentProvider通过相同的URI联系,实现数据共享.我重写了public class MyAdapter extends BaseAdapter,putD ...

  3. Android ContentProvider

    参考: http://www.cnblogs.com/linjiqin/archive/2011/05/28/2061396.html http://aijiawang-126-com.iteye.c ...

  4. Android ContentProvider支持跨进程数据共享与互斥、同步 杂谈

    在开发中,假如,A.B进程有部分信息需要同步,这个时候怎么处理呢?设想这么一个场景,有个业务复杂的Activity非常占用内存,并引发OOM,所以,想要把这个Activity放到单独进程,以保证OOM ...

  5. Android ContentProvider 内容提供者

    ContentProvider 内容提供者,是Android四大组件之一. 一.简述 可以理解为一个特殊的存储数据的类型,它提供了一套标准的接口来获取和操作数据.可以把数据封装到ContentProv ...

  6. Android ContentProvider 使用介绍

    contentProvider作为android四大组件其地位也是不可忽视的.contentProvider实现是在Sqlite数据库基础上实现的.其数据存储使用Sqlite实现,上层使用URI进行数 ...

  7. android contentprovider学习(遇到最满意的介绍)---阿冬专栏

    转自出处:http://blog.csdn.net/dmk877/article/details/50387741 1.什么是内容提供者? 首先我们必须要明白的是ContentProvider(内容提 ...

  8. android uri db,Android ContentProvider封装数据库和文件读写总结

    本文是我各处东拼西凑加上自己实现一个ContentProvider的使用总结,留做后用,主要介绍ContentProvider的集成方法. 一.综述 ContentProvider是Android四大 ...

  9. Android ContentProvider 完全解析及简单DEMO

    Android应用程序运行在不同的进程空间中,因此不同应用程序的数据是不能够直接访问的.为了增强程序之间的数据共享能力,Android系统提供了像SharedPreferences这类简单的跨越程序边 ...

最新文章

  1. 第三代测序技术的主要特点及其在病毒基因组研究中的应用
  2. android字符显示流程图,Android应用层View绘制流程与源码分析
  3. 软件测试用例设计“八法归一”——因果阵
  4. kvm.huge页、常用命令和桥接设置
  5. 数组反向遍历ios_iOS中数组遍历的方法及比较
  6. 下列哪个滤波器是非线性的_正确选择射频滤波器的八大窍门
  7. ubuntu11.04服务器安装
  8. Scala集合:reduce(化简)方法使用示例
  9. python列表写入csv文件_将多个列表写入csv。Python中的文件
  10. [html] 页面上如何显示特殊字符?
  11. PS基础学习 2---图层蒙版
  12. lintcode-135-数字组合
  13. AAAI2021-基于对比学习的三元组生成式抽取方法
  14. 高德地图显示多个气泡_arcgis api 4.x for js 地图加载多个气泡窗口展示(附源码下载)...
  15. 找到多个与名为“Login”的控制器匹配的类型
  16. fgo国服登陆显示服务器错误,命运冠位指定安装失败怎么办 FGO国服安装失败解决办法...
  17. 实现写邮箱html页面,用html写的简单的邮箱登陆界面
  18. 51nod 1521 一维战舰
  19. MATLAB在数学建模中的应用 随手笔记(二)----- 数据拟合方法
  20. 遗传算法(二)——编码

热门文章

  1. asp调用打开exe文件
  2. WCF与 Web Service的区别
  3. C#--封装、继承、多态
  4. Android activity之间的滑入切换
  5. 糟糕!HttpClient 连接池设置引发的一次雪崩!
  6. 做一个完整的Java Web项目太难了,因为这些你不会!
  7. 重构:改善饿了么交易系统的设计思路
  8. 理论+实践轻松掌握nohup java -jar xxxx.jar /dev/null 21 意义
  9. 我的编码习惯 —— API 接口定义
  10. 小谈 Java 单元测试