一种典型的Content Provider 代码架构
来源:互联网 发布:java做计算器 编辑:程序博客网 时间:2024/06/05 12:03
转自:http://www.eoeandroid.com/thread-48518-1-1.html
我们平时在做Android开发的时候,一定经常会接触到数据库操作,android使用sqlite作为它的本地数据库,并提供了一种叫做Content Provider的数据访问机制,简单来说,它就像一个web服务,有自己的URI,我们也是通过URI的形式来访问它的数据,通过这种形式的接口,使得我们的数据不仅在我们自己的应用中可以访问,甚至还可以被系统中的其他应用所调用。 一个典型的例子就是我们手机中的通讯录,android给我们暴露了一个接口,我们只要申请到相应的权限,通过访问这个接口,就可以得到通讯录的信息了。 说了这么多,现在言归正传,这篇文章主要是和大家分享一下Content Provider的实现方式,通过一些更加标准的代码架构,可以使我们的项目的效率更高,并且提高可维护性。
为了说明问题,我们只位这个表设置了三个字段,分别使记录的id,记事内容,和编辑时间, 大家需要注意个是id字段需要在前面加一个下划线,否则在和ListView绑定的时候会出问题。
现在有了表接口,那么我们就可以将这张表抽象程一个数据结构,代码如下:如上代码,我们定义好了我们需要的元数据,这个类首先定义了和ContentProvider相关的信息,AUTHORITY表示ContentProvider的URI,DATABASE_NAME和DATABASE_VERSION分别表示我们的数据库名和数据库版本。静态内部类NoteTableMetaData,表示我们刚才定义个表,首先TABLE_NAME表示表名,DEFAULT_SORT_ORDER 作为默认的排序规则,当然如果我们在查询中自定义了排序规则的话,这个就会被覆盖,CONTENT_URI定义了Provider对外访问的协议,CONTENT_TYPE,CONTENT_ITEM_TYPE用于表示两种uri模式的实体类型,这个其实很像我们http协议中的MIME类型。接下来的数据就是我们数据库字段的映射了,可以用注释标出他们的数据类型,这样,我们的元数据就完工拉,这个工作虽然有些枯燥,但是对于我们项目的可维护性还是很有帮助的,例如如果我们需要改动某个字段的名字,我们只需要修改元数据类就完成了所有的工作。
有了这些源数据,我们就可以开始来写ContentProvider了,首先,我们要定义一个默认的映射表,代码如下:这个HashMap其实就相当于我们select语句中的别名,在后面的sqlite操作中会用到这个数据,一般情况下,我们不需要更改别名,所以在map中将键和值都设置为同样的就可以了。
由于我们的NoteProvider要处理两种形式的URI,所以我们需要一个机制来区分不同的URI,这就要用到UriMatcher,代码如下:UriMatcher用来区分不同的URI,首先我们将它定义为一个静态属性,然后在静态初始化块中,使用它的addURI方法,为它添加了两个URI规则,并为每个规则设置了一个表示常量,这里有QUERY_LIST和QUERY_ITEM,而这个方法的前两个参数就是用来构造这个URI规则的,例如第一条规则中,第一个参数我们用到了元数据中的 AUTHORITY和一个notes字符串, 这样,这条规则最终就会成为这种形式org.spring.provider.NoteProvider/notes 而另外一条规则就是这样org.spring.provider.NoteProvider/notes/# ,注意到第二条规则中的井号,它是一个占位符,在实际的场景中,这个位置会用一个数字来代替。说了这么多,我们为什么要用两个URI来为这个Provider来提供接口呢,相信只要做过web开发的朋友就会知道,假如我们要做一个CRUD功能,我们首先需要一个页面来显示数据,这个页面是不接受ID参数的。但我们还需要有一个编辑功能的页面,而这个页面就需要接受一个ID来区分要编辑哪一条记录。这样就不难理解我们为什么要用两种URI了,这里的第一条URI规则,就相当于那个显示数据的页面,而第二个URI 中的井号的位置就相当于编辑页面中的ID。有了这个matcher后,我们就可以根据不同的URI开执行各自所需的操作。
现在Provider的基本信息已经基本完成了,因为我们的Provider需要和数据库进行交互,所以我们还需要一个中间层,可以使用SQLiteOpenHelper。如上代码,我们扩展了自己的Helper,并将它定义为Provider 的私有属性,这个类的实现中我们还是使用元数据来进行操作,例如创建数据库和更新数据库的操作,字段名和表名使用的是元数据中的属性。
现在有了这些基础架构后,我们就可以实现相应的数据库操作方法了,先从query说起:这里使用了SQLiteQueryBuilder来构建数据库查询,这样可使我们从更抽象的层次来处理数据库交互,在这里我们之前定义的matcher就派上用场拉,我们判断了一下URI的类型,如果使QUERY_LIST,就查询所有的数据,而如果是QUERY_ITEM类型,就只查询特定ID下的记录。这一点在我们的代码中应该很明显,当然如果给出的URI不符合任意一条规则,那么就直接抛出异常。 接下来我判断了一下这次查询是否明确指定了排序规则,如果没指定就使用我们之前定一个默认规则来对数据进行排序,随后就是获取数据库引用,并执行插叙操作了。 这里要注意一下c.setNotificationUri方法,这个方法为当的URI注册了一下通知,简单来说就是这样,如果有其他的调用改变了底层数据库并发送了更改消息,那么这些注册了通知的URI会自动更新他们的数据集,而不用我们手动的进行刷新,这样可以省去很多繁琐的编码工作。最典型的例子就是为ListView绑定数据时,如果使用了这种机制,我们在修改数据库中的数据后,ListView 的显示也会自动的刷新。
接下来介绍一下insert方法:先说一下这个方法传进来的参数,第一个参数时调用的URI,接下来的values就相当于insert语句中的列名和值的一对组合,由于这个插入方法只接受不带ID的URI所以我们在一开始的时候进行了一下判断,然后我们检测了一下是否指定了日期,如果没有就以当前时间作为默认值,随后就是数据库调用了。 我们通过返回rowID来判断该条记录是否插入成功,如果成功就返回带着这条记录ID的URI,否则就抛出异常。注意到这里的notifyChange方法,这个正好和前面的注册通知相对应,它会通知所有注册的URI,数据库已经改变。
下面再来介绍一下update方法:这里个方法里的内容和前面很类似,唯一的区别就是通过URI来确定更新的方式。这一点在代码中写的也比较明白,所以就不赘述了。最后再介绍一下delete 方法:这个方法也是判断两种不同的URI,如果时QUERY_LIST类型的URI,那么它就会删除所有的记录(当然,如果我们不需要这种操作,也可以忽略这种URI),另一种就是根据ID来删除相应的记录。这个方法应该也不难理解。 大家注意到,我们在这些方法中,大量的用到了我们元数据类中的信息,这样做的好处前面也说过了,隔离了底层的表结构后,让数据库结构的修改变得非常容易。
当然,我们还需要实现getType方法,来返回不同URI对应的MIME类型,这个信息,在我们的元数据中已有定义:好啦,到现在位置我们的Provider就已经实现好了,最后不要忘了在manifest中注册这个Provider:到此为止,我们的数据访问接口就实现完成了,我们可以用下面的方式很容易的进行数据访问:
这篇文章主要是给大家提供了一种ContentProvider的架构方法,通过一些良好的代码组织,可以让我们的开发工作变得更加轻松,并且有效的增强应用的建壮性,对我们日常的开发还是很有帮助的。当然这种方法并不是唯一的,更不敢说这个是最好的。更希望它能够起到一种抛砖引玉的作用,大家可以在这个基础之上进一步的探索,找出更加适合自己的架构方式~
以下是完整代码:
public class NoteProvider extends ContentProvider{ private static HashMap<String, String> noteProjectionMap; static { noteProjectionMap = new HashMap<String, String>(); noteProjectionMap.put(NoteTableMetaData.NOTE_ID, NoteTableMetaData.NOTE_ID); noteProjectionMap.put(NoteTableMetaData.NOTE_CONTENT, NoteTableMetaData.NOTE_CONTENT); noteProjectionMap.put(NoteTableMetaData.NOTE_PUB_DATE, NoteTableMetaData.NOTE_PUB_DATE); } private static UriMatcher matcher; private static final int QUERY_LIST = 1; private static final int QUERY_ITEM = 2; static { matcher = new UriMatcher(UriMatcher.NO_MATCH); matcher.addURI(NoteMetaData.AUTHORITY, "notes", QUERY_LIST); matcher.addURI(NoteMetaData.AUTHORITY, "notes/#", QUERY_ITEM); } private DatabaseHelper dbHelper; class DatabaseHelper extends SQLiteOpenHelper{ DatabaseHelper(Context context) { super(context, NoteMetaData.DATABASE_NAME, null, NoteMetaData.DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL( "create table " + NoteTableMetaData.TABLE_NAME + " ( " + NoteTableMetaData.NOTE_ID + " integer primary key, " + NoteTableMetaData.NOTE_CONTENT + " varchar(2000), " + NoteTableMetaData.NOTE_PUB_DATE + " integer" + ");" ); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("drop table if exists " + NoteTableMetaData.TABLE_NAME); onCreate(db); } } @Override public String getType(Uri uri) { switch (matcher.match(uri)) { case QUERY_LIST:{ return NoteTableMetaData.CONTENT_TYPE; } case QUERY_ITEM:{ return NoteTableMetaData.CONTENT_ITEM_TYPE; } default:{ throw new IllegalArgumentException("Unknown URI " + uri); } } } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); switch (matcher.match(uri)) { case QUERY_LIST:{ qb.setTables(NoteTableMetaData.TABLE_NAME); qb.setProjectionMap(noteProjectionMap); }break; case QUERY_ITEM:{ qb.setTables(NoteTableMetaData.TABLE_NAME); qb.setProjectionMap(noteProjectionMap); qb.appendWhere(NoteTableMetaData.NOTE_ID + "=" + uri.getPathSegments().get(1)); }break; default: throw new IllegalArgumentException("Unknown URI " + uri); } String orderBy; if(TextUtils.isEmpty(sortOrder)) { orderBy = NoteTableMetaData.DEFAULT_SORT_ORDER; }else { orderBy = sortOrder; } SQLiteDatabase db = dbHelper.getWritableDatabase(); Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy); c.setNotificationUri(getContext().getContentResolver(), uri); return c; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { SQLiteDatabase db = dbHelper.getWritableDatabase(); int count; switch (matcher.match(uri)) { case QUERY_LIST:{ count = db.delete(NoteTableMetaData.TABLE_NAME, selection, selectionArgs); }break; case QUERY_ITEM:{ String rowID = uri.getPathSegments().get(1); count = db.delete(NoteTableMetaData.TABLE_NAME, NoteTableMetaData.NOTE_ID + "=" + rowID + (!TextUtils.isEmpty(selection) ? (" and ( " + selection + " ) ") : "" ), selectionArgs); }break; default: throw new IllegalArgumentException("Unknown URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } @Override public Uri insert(Uri uri, ContentValues values) { if(matcher.match(uri) != QUERY_LIST) { throw new IllegalArgumentException("Unknown URI " + uri); } if(values.containsKey(NoteTableMetaData.NOTE_PUB_DATE) == false) { Long now = Long.valueOf(System.currentTimeMillis()); values.put(NoteTableMetaData.NOTE_PUB_DATE, now); } SQLiteDatabase db = dbHelper.getWritableDatabase(); long rowID = db.insert(NoteTableMetaData.TABLE_NAME, NoteTableMetaData.NOTE_CONTENT, values); if(rowID > 0) { Uri insertedUri = ContentUris.withAppendedId(NoteTableMetaData.CONTENT_URI, rowID); getContext().getContentResolver().notifyChange(insertedUri, null); return insertedUri; } throw new android.database.SQLException("Failed to insert row into " + uri); } @Override public boolean onCreate() { dbHelper = new DatabaseHelper(getContext()); return true; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { SQLiteDatabase db = dbHelper.getWritableDatabase(); int count; switch (matcher.match(uri)) { case QUERY_LIST:{ count = db.update(NoteTableMetaData.TABLE_NAME, values,selection, selectionArgs); }break; case QUERY_ITEM:{ String rowID = uri.getPathSegments().get(1); count = db.update(NoteTableMetaData.TABLE_NAME, values, NoteTableMetaData.NOTE_ID + "=" + rowID + (!TextUtils.isEmpty(selection) ? ("and ( " + selection + " ) ") : ""), selectionArgs); }break; default: throw new IllegalArgumentException("Unknown URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } }
0 0
- 一种典型的Content Provider 代码架构
- content provider 的使用
- Android的Content Provider
- Content Provider的加载
- Content Provider的权限
- content provider的使用!
- 简单的content provider
- Content Provider的加载
- Android AVD 出现No content provider found for permission revoke可能的一种解决方案
- 总结Content Provider的使用
- Android本地的Content Provider
- Android本地的Content Provider
- 总结Content Provider的使用
- 总结Content Provider的使用
- adnroid的Content Provider笔记
- 总结Content Provider的使用
- 总结Content Provider的使用
- android Content Provider的使用
- poj 2965 The Pilots Brothers' refrigerator (枚举)
- Android的单位以及屏幕分辨率详解
- zoj 1158 判断2线段完全相交
- 移植tiny210的 触摸屏驱动
- Android开发中处理图片OOM (OutOfMemoryError) 的若干方法小结
- 一种典型的Content Provider 代码架构
- 云安全三大趋势:纵深防御、软件定义安全、设备虚拟化
- 闲来看看View.java的Developer Guides
- IE浏览器 应用程序发生异常 未知的软件异常(0x0000417),位置为 0x6546120
- 设计模式 - 迭代器模式(iterator pattern) Java 迭代器(Iterator) 详解
- 翻页工具类
- OCP-1Z0-051-题目解析-第4题
- 关于CString字符类型
- Android开发之Frame动画实现方法