最近查看了Android Sdk中提供的NoteList示例代码,里面有一个ContentProvider的示例蛮经典的,所以进行下结构分析,让印象更深刻些,免得以后把较为复杂的ContentProvider用法弄忘了。^-^。

转载请注明出处:http://blog.csdn.net/nieweilin

一、字段部分

代码:

1 publicclassNotePadProviderextendsContentProvider{
2
3 privatestaticfinalStringTAG="NotePadProvider";
4
5 privatestaticfinalStringDATABASE_NAME="note_pad.db";
6 privatestaticfinalintDATABASE_VERSION=2;
7 privatestaticfinalStringNOTES_TABLE_NAME="notes";
8
9 privatestaticHashMapString,String>sNotesProjectionMap;
10 privatestaticHashMapString,String>sLiveFolderProjectionMap;
11
12 privatestaticfinalintNOTES=1;
13 privatestaticfinalintNOTE_ID=2;
14 privatestaticfinalintLIVE_FOLDER_NOTES=3;
15
16 privatestaticfinalUriMatchersUriMatcher;
3,TAG,用于在android程序运行时在Log中显示NotePadProvider这个字符串,方便程序的调试用
5,DATABASE_NAME,表征你要使用的数据库名字,这里是note_pad.db,也就是说会在应用程序的数据文件夹里面生成一个名字为note_pad.db的Sqlite数据库文件。
6,DATABASE_VERSION,表征你的数据库的版本情况。具体为什么是2不是很明白。
7,NOTES_TABLE_NAME,数据库中的一个表的表名。这里这个表的名字是notes。
8,这个map在以后SQLiteQueryBuilder的setProjectionMap方法中会用到,起到的作用就是将数据库中表的名字进行一个映射。刚开始我不是很懂这一部分的意义,后来看sdk文档中有这样一句话:
The projection map maps from column names that the caller passes into query to database column names. This is useful for renaming columns as well as disambiguating column names when doing joins. For example you could map "name" to "people.name". If a projection map is set it must contain all column names the user may request, even if the key and value are the same.
这个map是对用户查询操作传入的column名和数据表中存在的column名进行了一个映射,这样一来,有些数据库中抽象的表的column名就可以通过这个projection map实现重新命名,以达到方便开发者做一些类似于数据库表联接(join)操作的目的。一般情况下数据库中的表的column名就已经够清晰了,所以你可以不用再在projection map里面做一些column重命名的操作了——也就是说,你可以将这个map对象中的key和value都设为同样的值(接下来的代码会有体现)。
10,这个map和刚才那个同样是一个projection map,不同的是它针对的是Notepad的Live Folder。至于什么是Live Folder,请参看Android sdk文档中的Resources中的Live Folders一文。
12,13,14是三个常量,它们用于等一下会出现的UriMatcher类的addUri()方法。实际上它们是UriMatcher的match()方法的返回值,用于标记Uri的类型。从名字就可以知道,NOTES标记所有的记载(NOTE)所对应的URI返回值,NOTE_ID对应单个NOTE的URI返回值,LIVE_FOLDER_NOTES代表的是Live Folder中的NOTE对应的URI的返回值。它们三个标记了NoteList应用中基本的几种URI,从而在UriMatcher执行match()方法时,便会返回这些相应的int值。
16,ContentProvider封装的一个UriMatcher实例。该类用于将特定类型的Uri和一些int值进行映射式绑定,然后以后通过该类判断Uri类型时,返回这个绑定的int值,从而可以由此对Uri的类型判断提供更为有效的手段。
二、静态初始化语句块部分
代码:
1 static{
2 sUriMatcher=newUriMatcher(UriMatcher.NO_MATCH);
3 sUriMatcher.addURI(NotePad.AUTHORITY,"notes",NOTES);
4 sUriMatcher.addURI(NotePad.AUTHORITY,"notes/#",NOTE_ID);
5 sUriMatcher.addURI(NotePad.AUTHORITY,"live_folders/notes",LIVE_FOLDER_NOTES);
6
7 sNotesProjectionMap=newHashMapString,String>();
8 sNotesProjectionMap.put(Notes._ID,Notes._ID);
9 sNotesProjectionMap.put(Notes.TITLE,Notes.TITLE);
10 sNotesProjectionMap.put(Notes.NOTE,Notes.NOTE);
11 sNotesProjectionMap.put(Notes.CREATED_DATE,Notes.CREATED_DATE);
12 sNotesProjectionMap.put(Notes.MODIFIED_DATE,Notes.MODIFIED_DATE);
13
14 //SupportforLiveFolders.
15 sLiveFolderProjectionMap=newHashMapString,String>();
16 sLiveFolderProjectionMap.put(LiveFolders._ID,Notes._ID+"AS"+
17 LiveFolders._ID);
18 sLiveFolderProjectionMap.put(LiveFolders.NAME,Notes.TITLE+"AS"+
19 LiveFolders.NAME);
20 //AddmorecolumnshereformorerobustLiveFolders.
21 }
static语句模块经常被用来初始化类里面的字段部分。这里我们的sUriMatcher、sNotesProjectionMap、sLiveFolderProjectionMap都是一次初始化,以后基本上都无需改变的,所以我们就放到这里来进行静态static语句块的初始化了。
2-5进行sUriMatcher的初始化工作。需要注意的是NotePad.AUTHORITY是本ContentProvider所对应的Uri(这个Uri是整个Android中唯一标识的,由开发人员在AndroidManifest.xml中定义的)。后面类似notes,notes/#,live_folders/notes等便是在这个Uri上添加的部分,用于对应于全部note,单个note以及live folder中的note。在这里初始化了之后,以后调用sUriMatcher的match()方法时,就会返回对应的NOTES,NOTE_ID,LIVE_FOLDER_NOTES等int值,从而表示该Uri对应的种类。
7-12对sNotesProjectionMap进行了初始化,这里没有对column进行rename。而15-19行则对sLiveFolderProjectionMap初始化时进行了rename。

三,一个SQLiteOpenHelper

代码:

1 /**
2 *Thisclasshelpsopen,create,andupgradethedatabasefile.
3 */
4 privatestaticclassDatabaseHelperextendsSQLiteOpenHelper{
5
6 DatabaseHelper(Contextcontext){
7 super(context,DATABASE_NAME,null,DATABASE_VERSION);
8 }
9
10 @Override
11 publicvoidonCreate(SQLiteDatabasedb){
12 db.execSQL("CREATETABLE"+NOTES_TABLE_NAME+"("
13 +Notes._ID+"INTEGERPRIMARYKEY,"
14 +Notes.TITLE+"TEXT,"
15 +Notes.NOTE+"TEXT,"
16 +Notes.CREATED_DATE+"INTEGER,"
17 +Notes.MODIFIED_DATE+"INTEGER"
18 +");");
19 }
20
21 @Override
22 publicvoidonUpgrade(SQLiteDatabasedb,intoldVersion,intnewVersion){
23 Log.w(TAG,"Upgradingdatabasefromversion"+oldVersion+"to"
24 +newVersion+",whichwilldestroyallolddata");
25 db.execSQL("DROPTABLEIFEXISTSnotes");
26 onCreate(db);
27 }
28 }
29
30 privateDatabaseHelpermOpenHelper;
这里实现了一个继承于SQLiteOpenHelper的DatabaseHelper内部类。这个类被封装在这里是因为这个ContentProvider本身是和数据库进行打交道的,所以需要一个SQLiteOpenHelper来处理相关关系。具体该类怎么用自己Google吧。

四,方法部分

代码:

1 @Override
2 publicbooleanonCreate(){
3 mOpenHelper=newDatabaseHelper(getContext());
4 returntrue;
5 }
该方法用于对ContentProvider中的资源进行初始化,可以知道的是,这些处理是不可能在static语句块中实现的,因为会有一些要求,例如这里我们要传入一个Context实例给DataBaseHelper构造器,这在static语句块中是实现不了的。
1 @Override
2 publicCursorquery(Uriuri,String[]projection,Stringselection,String[]selectionArgs,
3 StringsortOrder){
4 SQLiteQueryBuilderqb=newSQLiteQueryBuilder();
5 qb.setTables(NOTES_TABLE_NAME);
6
7 switch(sUriMatcher.match(uri)){
8 caseNOTES:
9 qb.setProjectionMap(sNotesProjectionMap);
10 break;
11
12 caseNOTE_ID:
13 qb.setProjectionMap(sNotesProjectionMap);
14 qb.appendWhere(Notes._ID+"="+uri.getPathSegments().get(1));
15 break;
16
17 caseLIVE_FOLDER_NOTES:
18 qb.setProjectionMap(sLiveFolderProjectionMap);
19 break;
20
21 default:
22 thrownewIllegalArgumentException("UnknownURI"+uri);
23 }
24
25 //Ifnosortorderisspecifiedusethedefault
26 StringorderBy;
27 if(TextUtils.isEmpty(sortOrder)){
28 orderBy=NotePad.Notes.DEFAULT_SORT_ORDER;
29 }else{
30 orderBy=sortOrder;
31 }
32
33 //Getthedatabaseandrunthequery
34 SQLiteDatabasedb=mOpenHelper.getReadableDatabase();
35 Cursorc=qb.query(db,projection,selection,selectionArgs,null,null,orderBy);
36
37 //Tellthecursorwhaturitowatch,soitknowswhenitssourcedatachanges
38 c.setNotificationUri(getContext().getContentResolver(),uri);
39 returnc;
40 }
这个方法是我们的ContentProvider必须实现的,最重要的方法之一。当我们通过ContentProvider进行查询操作时,这个方法就会被回调,而且返回一个保存了查询结果的Cursor对象。
对于参数:
uri,查询时传入的uri,它代表了我们要查找的数据源。
projection,查询时传入的一个字符串数组,通过它来表明我们需要数据表中的哪些row。当查询结束后就会返回包含这些row的结果表。
selectionArgs,功能不清楚,猜想为表明结果表中各个row的排列顺序。
sortOrder,表示返回的结果集的排列顺序的语句。
方法体内:
4,声明了一个SQLiteQueryBuilder对象,该对象利用传入的参数生成一个完整的sqlite查询语句,还可以完成查询工作并返回结果集(Cursor)。
5,为该SQLiteQueryBuilder对象设置了它查询工作将要针对的表。
7-23通过一个switch语句,完成了通过uri种类的辨识来生成不同的SQLiteQueryBuilder对象的工作。而这里的uri辨识便用到了我们刚开始声明的UriMatcher对象。其中14行为SQLiteQueryBuilder设置了一个where查询条件。9,13,18则分别为不同的uri设置了不同的projection map。
27-31设置了查询返回结果的顺序。TextUtils.isEmpty(sortOrder)这一句判断传入的sortOrder是否为空字符串(即"")或者null。如果是的话就将orderBy设置为一个定义好的默认的顺序。否则用传入的顺序。
34,通过我们之前定义的SQLiteDatabase类来获得一个可读不可写的数据库对象。
35,通过我们刚刚构造的SQLiteQueryBuilder来进行查询操作,并且返回一个我们即将return的Cursor对象。
38,让Cursor来监视它所属的那个uri。
39,返回Cursor c,让外界通过它来获得查询结果。
1 @Override
2 publicStringgetType(Uriuri){
3 switch(sUriMatcher.match(uri)){
4 caseNOTES:
5 caseLIVE_FOLDER_NOTES:
6 returnNotes.CONTENT_TYPE;
7
8 caseNOTE_ID:
9 returnNotes.CONTENT_ITEM_TYPE;
10
11 default:
12 thrownewIllegalArgumentException("UnknownURI"+uri);
13 }
14 }
这个方法用于返回相关uri对应的MIME类型。这个类型基本上是自己定义的,而且是会用在Activity的启动判别中去的。需要注意的是,这里又用到了我们之前定义的uri判别工具——sUriMatcher 。
1 @Override
2 publicUriinsert(Uriuri,ContentValuesinitialValues){
3 //Validatetherequesteduri
4 if(sUriMatcher.match(uri)!=NOTES){
5 thrownewIllegalArgumentException("UnknownURI"+uri);
6 }
7
8 ContentValuesvalues;
9 if(initialValues!=null){
10 values=newContentValues(initialValues);
11 }else{
12 values=newContentValues();
13 }
14
15 Longnow=Long.valueOf(System.currentTimeMillis());
16
17 //Makesurethatthefieldsareallset
18 if(values.containsKey(NotePad.Notes.CREATED_DATE)==false){
19 values.put(NotePad.Notes.CREATED_DATE,now);
20 }
21
22 if(values.containsKey(NotePad.Notes.MODIFIED_DATE)==false){
23 values.put(NotePad.Notes.MODIFIED_DATE,now);
24 }
25
26 if(values.containsKey(NotePad.Notes.TITLE)==false){
27 Resourcesr=Resources.getSystem();
28 values.put(NotePad.Notes.TITLE,r.getString(android.R.string.untitled));
29 }
30
31 if(values.containsKey(NotePad.Notes.NOTE)==false){
32 values.put(NotePad.Notes.NOTE,"");
33 }
34
35 SQLiteDatabasedb=mOpenHelper.getWritableDatabase();
36 longrowId=db.insert(NOTES_TABLE_NAME,Notes.NOTE,values);
37 if(rowId>0){
38 UrinoteUri=ContentUris.withAppendedId(NotePad.Notes.CONTENT_URI,rowId);
39 getContext().getContentResolver().notifyChange(noteUri,null);
40 returnnoteUri;
41 }
42
43 thrownewSQLException("Failedtoinsertrowinto"+uri);
44 }
这个方法用于我们的ContentProvider进行insert操作。参数uri为我们即将进行insert操作的数据源。initialValues则是我们传入的要insert到数据库中去的信息。该方法返回我们insert进去的条目所对应的uri。
4-6进行异常判断,如果传入的uri不是我们想要的则抛出异常。
8,声明了一个本地的ContentValues,用于接下来的initialValues处理。
9-13,如果initialValues为null,则本地生成一个ContentValues对象,否则使用initialValues。
15,通过方法调用获得了一个时间信息,将它保存到Long对象now里面去。now作为时间特性将会保存到数据库中的CREATED_DATE和MODIFIED_DATE项目中去。
18-33,通过containsKey方法来判断我们的values里面是否包含了某一项条目,有则无动作,没有则通过本地获得信息并且放到values里面去。
35,获得一个可读可写的数据库对象。
36,执行insert操作,并且把insert方法返回的我们insert进去的项的_id保存在rowId中。
37,通过rowId来生成一个即将返回的uri。
1 @Override
2 publicintdelete(Uriuri,Stringwhere,String[]whereArgs){
3 SQLiteDatabasedb=mOpenHelper.getWritableDatabase();
4 intcount;
5 switch(sUriMatcher.match(uri)){
6 caseNOTES:
7 count=db.delete(NOTES_TABLE_NAME,where,whereArgs);
8 break;
9
10 caseNOTE_ID:
11 StringnoteId=uri.getPathSegments().get(1);
12 count=db.delete(NOTES_TABLE_NAME,Notes._ID+"="+noteId
13 +(!TextUtils.isEmpty(where)?"AND("+where+')':""),whereArgs);
14 break;
15
16 default:
17 thrownewIllegalArgumentException("UnknownURI"+uri);
18 }
19
20 getContext().getContentResolver().notifyChange(uri,null);
21 returncount;
22 }
23
24 @Override
25 publicintupdate(Uriuri,ContentValuesvalues,Stringwhere,String[]whereArgs){
26 SQLiteDatabasedb=mOpenHelper.getWritableDatabase();
27 intcount;
28 switch(sUriMatcher.match(uri)){
29 caseNOTES:
30 count=db.update(NOTES_TABLE_NAME,values,where,whereArgs);
31 break;
32
33 caseNOTE_ID:
34 StringnoteId=uri.getPathSegments().get(1);
35 count=db.update(NOTES_TABLE_NAME,values,Notes._ID+"="+noteId
36 +(!TextUtils.isEmpty(where)?"AND("+where+')':""),whereArgs);
37 break;
38
39 default:
40 thrownewIllegalArgumentException("UnknownURI"+uri);
41 }
42
43 getContext().getContentResolver().notifyChange(uri,null);
44 returncount;
45 }
46
47
48 }
delete方法和update方法则针对的是数据库中的删除条目和更新条目的操作看懂之前的代码后这里没有难度,所以就不解释了。
四,小结
在我的理解看来,ContentProvider是对各种数据源进行统一包装并外露的工具——不只是我们这里所用到的典型的数据库式的数据源,其他如文件等数据源也可以通过ContentProvider来包装并实现对其他有需求的消费者的数据供应。虽然对于初学者来说ContentProvider是较为难以理解的,但当你用熟后,你便定会感叹于Android系统的细节精妙性!
本文撰于2010-1-27晚10点华中科技大学IDream工作室,正值小年,屋外爆竹声声。明天回家,看爸妈。

一个ContentProvider的典型结构分析相关推荐

  1. 使用一个ContentProvider操作多张表

    使用一个ContentProvider操作多张表 在我们开发过程中,通常使用ContentProvider来操作数据.查询数据库中的一张表的数据我们创建一个ContentProvider提供操作,需要 ...

  2. 完整的开发一个ContentProvider步骤

    1.定义自己的ContentProvider类,该类需要继承Android提供的ContentProvider基类. 2.向Android系统注册这个"网站",也就是在Androi ...

  3. 一个通证经济典型案例——移动广告矿机

    最近一段时间,我一直在谈一个问题,就是通证经济的适用条件.精炼又精炼之后,我将这个条件描述为"边际效率递增".具体地说,如果有一群人通过互联网建立协作网络,做某一件能够创造价值的事 ...

  4. Android插件化原理解析——ContentProvider的插件化

    目前为止我们已经完成了Android四大组件中Activity,Service以及BroadcastReceiver的插件化,这几个组件各不相同,我们根据它们的特点定制了不同的插件化方案:那么对于Co ...

  5. ​UG塑胶模具设计结构分析是如何挤压成型的

    ​UG塑胶模具设计结构分析是如何挤压成型的 概 述 塑料挤出成型是用加热的方法使塑料成为流动状态,然后在一定压力的作用下使它通过塑模,经定型后制得连续的型材.挤出法加工的塑料制品种类很多,如管材.薄膜 ...

  6. 四大组件之ContentProvider(一)-使用系统提供的ContentProvider

    更新时间 修改意见 2016-08-02 陈敏 第1节 ContentProvider介绍 ContentProvider是安卓系统的四大组件之一,可以向其他组件提供数据访问的能力.它就像是一个网站, ...

  7. android contentprovider 插件化之一

    ------本文转载自 Android插件化原理解析--contentprovider的插件化      这一系列的文章实在是写的好! 1, 概述 目前为止我们已经完成了Android四大组件中 Ac ...

  8. ContentProvider插件化

    ContentProvider插件化 目前为止我们已经完成了Android四大组件中Activity,Service以及BroadcastReceiver的插件化,这几个组件各不相同,我们根据它们的特 ...

  9. 插件化原理解析——ContentProvider的插件化

    目前为止我们已经完成了Android四大组件中Activity,Service以及BroadcastReceiver的插件化,这几个组件各不相同,我们根据它们的特点定制了不同的插件化方案:那么对于Co ...

最新文章

  1. 成功解决xgboost\core.py, ValueError: feature_names may not contain [, ] or
  2. Redis单机部署、添加开机自启、配置参数
  3. c/c++ 字符串分割
  4. 【STC15库函数上手笔记】9、硬件SPI
  5. 全面容器化:阿里5年带给我的最大收获
  6. php 面向对象基础,PHP 面向对象基础
  7. 小程序切换账户拉取仓库文件的appid提示
  8. 二叉查找树--插入、删除、查找
  9. kafka的消费顺序_Kafka原理和实践云平台技术栈13
  10. 微分方程计算机仿真国内外研究论文,微分方程数值解法毕业论文--开题报告.doc...
  11. 使用版本控制软件TortoiseSVN对程序和文档进行控制的说明
  12. win7如何设置通电自动开机_win7系统设置自动开机
  13. 《运动改造大脑》总结
  14. GPT分区是什么?如何创建GPT分区
  15. 关于打开网页FLASH显示叉叉问题
  16. 怎样测试企业级SSD
  17. 老笔记本机械硬盘换固态装系统,再战10年!
  18. 2018-2019-2 20175317 实验三《敏捷开发与XP实践》实验报告
  19. 【算法•日更•第七期】区间动态规划详解+一本通1570能量项链题解
  20. 组件数据更新页面未更新的解决方法

热门文章

  1. Android字数限制的EditText实现方案研究
  2. Ubuntu下vsftp安装和配置
  3. Keepalived原理详解
  4. linux,想说爱你真的很不容易!
  5. 读取FTP上的excel文件,并写入数据库
  6. Web开发中的弹出对话框控件介绍
  7. ROS机器人程序设计(原书第2版)3.1.2 ROS节点启动时调用gdb调试器
  8. idea git 注意事项
  9. spring整合shiro
  10. WebStorm安装Vim以及快捷键设置