Android的ContentProvider(一)

来源:互联网 发布:小区网络监控设计方案 编辑:程序博客网 时间:2024/05/19 03:26

这里描述的自然是自定义ContentProvider,不讲原理,只讲实现。

实现ContentProvider有以下几个步骤:

1. 继承android.content.ContentProvider实现自定义ContentProvider;

2. 在AndroidManefist.xml中注册ContentProvider;

3. 使用自定义ContentProvider。


建立类BookContentProvider,令其继承android.content.ContentProvider,此时会要求你重写以下几个方法:

1. onCreate():你可以理解为打开数据库连接;
2. query(Uri, String[], String, String[], String):查询方法;
3. getType(Uri):返回给定URI的MimeType类型,等我知道什么意思了再贴出来,不要在意这些细节;
4. insert(Uri, ContentValues):插入方法;
5. delete(Uri, String, String[]):删除方法; 
6. update(Uri, ContentValues, String, String[]):更新方法。

首先说到onCreate,在Android中,或者说在本例中,我们用Sqlite,所以打开数据库连接,其实是打开Sqlite,为此我们需要自定义一个android.database.sqlite.SQLiteOpenHelper的子类,如下所示:

public class DBHelper extends SQLiteOpenHelper {public DBHelper(Context context) {super(context, Book.DB_FILE_NAME, null, Book.DB_VERSION);}@Overridepublic void onCreate(SQLiteDatabase db) {StringBuilder builder = new StringBuilder();builder.append(" CREATE TABLE ");builder.append(Book.TABLE_NAME);builder.append(" ( ");builder.append(Book.Columns._ID);builder.append(" INTEGER PRIMARY KEY, ");builder.append(Book.Columns.NAME);builder.append(" TEXT, ");builder.append(Book.Columns.AUTHOR);builder.append(" TEXT, ");builder.append(Book.Columns.CREATE_TIME);builder.append(" INTEGER ");builder.append(" ); ");db.execSQL(builder.toString());}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {db.execSQL("DROP TABLE IF EXISTS ".concat(Book.TABLE_NAME));onCreate(db);}}

实现了俩方法,一个是onCreate,其实就是执行建表命令,一个是onUpgrade,即更新表的版本。

在onCreate中调用了Book的一些常量,Book类其实是对我们要用的数据库的一些基本信息的描述,这些属性会在下文中被用到,现在先贴出来:

/** *  * Dec 22, 2014 3:42:39 PM * @Geloin * */package com.geloin.baseopera.provider;import android.net.Uri;import android.provider.BaseColumns;/** *  * Dec 22, 2014 3:42:39 PM *  * @Geloin *  */public class Book {public static final String TABLE_NAME = "books";public static final String DB_FILE_NAME = "books.db";public static final Integer DB_VERSION = 1;public static final String AUTHORITY = "com.geloin.baseopera.provider.BookProvider";public static final String DEFAULT_ORDER = "createTime DESC";public static final Uri CONTENT_URI = Uri.parse("content://".concat(AUTHORITY).concat("/books"));/** * 列名 *  * Dec 22, 2014 3:52:25 PM *  * @Geloin *  */public class Columns implements BaseColumns {public static final String NAME = "name";public static final String AUTHOR = "author";public static final String CREATE_TIME = "createTime";}/** * ContentType *  * Dec 22, 2014 3:53:40 PM *  * @Geloin *  */public class ContentType {/** * 集合 */public static final String COLL_TYPE = "vnd.android.cursor.dir/vnd.androidbook.book";/** * 单项 */public static final String ITEM_TYPE = "vnd.android.cursor.item/vnd.androidbook.book";}}

还是说回onCreate方法,onCreate方法作用是打开连接,所以其实现如下:

@Overridepublic boolean onCreate() {dbHelper = new DBHelper(getContext());return true;}

接下来是查询方法query,它的参数是:

1. uri:访问地址,首先得知道,ContentProvider是通过Uri访问的;

2. projection:查询结果返回哪些列,我们把列名都定义在了Book的Columns中;

3. selection:查询条件,参考sqlite;

4. selectionArgs:查询条件中每个“?”对应的值,有序的;

5. sortOrder:当然是排序方式。

顺便说一下MimeType,据我所知,ContentProvider中有两种MimeType,一种是针对全库的,一种是针对某一条记录的,针对全库的类似于“content://com.oppo.baseopera.provider.BookProvider/books”,针对某一条记录的则类似于“content://com.oppo.baseopera.provider.BookProvider/books/3”,最后一个3表示ID为3的记录。

我们在Book中定义了这两种MimeType(ContentType),分别标识为“COLL_TYPE”和“ITEM_TYPE”,而这两种ContentType是在Uri中体现的,如query中的uri,所以我们需要使用一个方式来区分这两种ContentType,android.content.UriMatcher提供了相关的方法,不作具体解释,看代码:

private static UriMatcher uriMatcher;private static final int COLL_TYPE_URI_MATCHER = 1;private static final int ITEM_TYPE_URI_MATCHER = 2;static {uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);uriMatcher.addURI(Book.AUTHORITY, "books", COLL_TYPE_URI_MATCHER);uriMatcher.addURI(Book.AUTHORITY, "books/#", ITEM_TYPE_URI_MATCHER);}

定义UriMatcher后,在query方法中即可使用UriMatcher来判断传入的Uri适配哪种MimeType:

@Overridepublic Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {SQLiteQueryBuilder qb = new SQLiteQueryBuilder();switch (uriMatcher.match(uri)) {case COLL_TYPE_URI_MATCHER: {qb.setTables(Book.TABLE_NAME);qb.setProjectionMap(columnMap);break;}case ITEM_TYPE_URI_MATCHER: {qb.setTables(Book.TABLE_NAME);qb.setProjectionMap(columnMap);qb.appendWhere(Book.Columns._ID.concat("=").concat(uri.getPathSegments().get(1)));break;}default: {throw new SQLException("Unknown URI:" + uri);}}String orderBy = null;if (TextUtils.isEmpty(sortOrder)) {orderBy = Book.DEFAULT_ORDER;} else {orderBy = sortOrder;}SQLiteDatabase db = dbHelper.getReadableDatabase();Cursor c = db.query(Book.TABLE_NAME, projection, selection,selectionArgs, null, null, orderBy);// 通知数据库变化c.setNotificationUri(getContext().getContentResolver(), uri);return c;}
这里有些代码可以解释一下:

1. SQLiteQueryBuilder可以理解成创建SQL语句,查询所有库时直接把表名及列对应关系传入即可,查询某个ID(也就是ITEM_TYPE时),则需要再指定ID;

2. 通过dbHelper打开可读连接,再调用query查询即可;

3. setNotificationUri是通知数据库变化,不仅仅是query,insert、delete、update中都最好调用一下。

上面讲到列对应关系,其实是把传入的projection与Book的列对应起来,列对应关系是一个Map对象,如下所示:

private static Map<String, String> columnMap;static {columnMap = new HashMap<String, String>();columnMap.put(Book.Columns._ID, Book.Columns._ID);columnMap.put(Book.Columns.NAME, Book.Columns.NAME);columnMap.put(Book.Columns.AUTHOR, Book.Columns.AUTHOR);columnMap.put(Book.Columns.CREATE_TIME, Book.Columns.CREATE_TIME);}

query返回的是一个游标,后面将讲述如何取得游标中的数据。


0 0