Content Provider
提供了统一对外的数据访问接口,可以把自己应用的数据提供出去给的应用程序使用,可以对你的数据进行增删改查,例如 通讯录就是其中的一种。
要想创建一个 Content Provider 需要进行下面几部操作:
- 继承 ContentProvider 类,重写 onCreate、getType、insert、update、query、delete 方法。
- 在 AndroidManifest.xml 文件中对 Provider 进行注册。
<provider android:name="classpath" android:authorities="com.exmaple.provider.applicationname" />
- 发布 Content Provider 的 URI 地址,每个Content Provider 都应该使用一个公有的静态 CONTENT_URI 属性来公开它的授权
Public static final Uri CONTENT_URI = Uri.parse("content://com.exmaple.provider.applicationname/elements")
通过 Android 中的 Uri 访问 Content Provider,Uri主要包含下面几部分:content://com.example.earthquake/contentprovider/quakes/1
- scheme:Content Provider 的 scheme 是 content://
- content:是 Content Provider 的唯一标示,外部程序可以根据这个标示找到对应的 Content Provider
- path:标示我们要操作的数据
我们一般提供全部查询和指定条数两种支持查询模式:其中一个非常有用的类 UriMatcher,它是一个非常有用的类,可以分析 URI 并确定它的形势。
// 创建两个常量来区分不同的 URI 请求
private static final int ALLROWS = 1;
private static final int SINGLE_ROW = 2;
private static final UriMatcher uriMatcher;
// 填充 UriMatcher 对象,其中 element 结尾的 URI 对应请求所有的数据
// 以 elements/rowid 结尾的 URI 代表请求单行数据
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI("com.exmaple.message.provider", "elements", ALLROWS);
uriMatcher.addURI("com.exmaple.message.provider", "elements/#", SINGLE_ROW);
}
在同一个 Content Provider 中,可以使用同样的技术来公开其他的 URI,这些 URI 代表了不同的数据子集或数据库中不同的表。
区分了全表和单行查询后,就可以很容易的使用 SQLiteQueryBuilder 类对一个查询应用额外的选择条件,如下面的代码所示:
SQLiteQueryBuilder sqb = new SQLiteQueryBuilder();
// 如果是行查询,用传入的行限制结果集
switch (uriMatcher.match(uri)) {
case SINGLE_ROW:
String rowID = uri.getPathSegments().get(1);
sqb.appendWhere(KEY_ID + "=" + rowID);
default:
break;
}
常用帮助方法:
UriMatcher:用于匹配对应的 Uri 路径
ContentUris:用于操作 Uri 路径后面的 id 部分
- withAppendId 通过该方法把 主 Uri 和 对应的 id 传入进去,组装返回对应的 uri 对象
当应用程序启动时,每个 Content Provider 的 onCreate 处理程序会再应用程序的主线程中被调用。
和之前的数据库操作一样,最好使用 SQLiteOpenHelper 来延迟打开(必要的地方创建)底层的数据库,直到 Content Provider 的查询或事务方法需要时再打开或创建它。
@Override
public boolean onCreate() {
// 构建一个底层数据库
// 延迟打开数据库,直到需要执行
// 一个查询或者事务时再打开
sqLiteHelper = new MessageSQLiteHelper(getContext(),
MessageSQLiteHelper.DATABASE_NAME, null,
MessageSQLiteHelper.DATABASE_VERSION);
return true;
}
实现 Content Provider 查询
要想使用 Content Provider 就必须实现 query 和 getType 方法。Content Provider 使用这些方法来访问底层数据,无需知道底层的数据结构和实现。
Content Provider 的最常见场景就是访问一个 SQLite 数据库,但在这些方法中,你可以访问任何的数据库(包括文件或应用程序实例变量)。
UriMatcher 对象应用于完善事务处理和查询请求,而 SQLiteQueryBuilder 是执行基于行查询的便利辅助类。
public class StudentContentProvider extends ContentProvider {
private SimpleSQLiteHelper sqLiteHelper;
// 创建两个常量来区分不同的 URI 请求
public static final int ALLROWS = 1;
public static final int SINGLE_ROW = 2;
public static final Uri CONTENT_URI = Uri.parse("content://com.example.contentprovider/students");
private static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI("com.example.contentprovider", "students", ALLROWS);
uriMatcher.addURI("com.example.contentprovider", "students/#",
SINGLE_ROW);
}
@Override
public boolean onCreate() {
sqLiteHelper = new SimpleSQLiteHelper(getContext(),
SimpleSQLiteHelper.DATABASE_NAME, null,
SimpleSQLiteHelper.DATABASE_VERSION);
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db;
try {
db = sqLiteHelper.getWritableDatabase();
} catch (SQLiteException e) {
db = sqLiteHelper.getReadableDatabase();
}
// 设定分组和聚合条件
String groupBy = null;
String having = null;
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
switch (uriMatcher.match(uri)) {
case SINGLE_ROW:
String rowID = uri.getPathSegments().get(1);
queryBuilder.appendWhere(SimpleSQLiteHelper.KEY_ID + " = " + rowID);
break;
default:
break;
}
// 指定要执行查询的表,根据需要,这可以是一个特定的表或者一个连接
queryBuilder.setTables(SimpleSQLiteHelper.DATABASE_TABLE);
// 执行查询操作
Cursor cursor = queryBuilder.query(db, projection, selection,
selectionArgs, groupBy, having, sortOrder);
return cursor;
}
@Override
public String getType(Uri uri) {
// 为一个 Content Provider URI 返回一个字符串,它标识了 MIME 类型
switch (uriMatcher.match(uri)) {
case ALLROWS:
return "vnd.android.cursor.dir/vnd.example.contentprovider.elemental";
case SINGLE_ROW:
return "vnd.android.cursor.item/vnd.example.contentprovider.elemental";
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// 打开一个可写的数据库连接
SQLiteDatabase db = sqLiteHelper.getWritableDatabase();
String nullColumnHack = null;
// 向表中插入值
long id = db.insert(SimpleSQLiteHelper.DATABASE_TABLE, nullColumnHack,
values);
if (id > -1) {
// 构造并返回插入行的 URI
Uri insertedId = ContentUris.withAppendedId(CONTENT_URI, id);
// 通知所有的观察者,数据集已经改变
getContext().getContentResolver().notifyChange(insertedId, null);
return insertedId;
}
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// 打开一个可写的数据库连接
SQLiteDatabase db = sqLiteHelper.getWritableDatabase();
// 如果是行 URI,限定删除行为指定的行
switch (uriMatcher.match(uri)) {
case SINGLE_ROW:
String rowID = uri.getPathSegments().get(1);
selection = SimpleSQLiteHelper.KEY_ID + " = " + rowID
+ (!TextUtils.isEmpty(selection) ? " AND (" + selection + ")" : "");
break;
default:
break;
}
// 要想返回删除的项的数量,必须指定一条 where 子句,要删除所有的行并返回一个值,则传入 1
if (selection == null) {
selection = "1";
}
// 执行删除操作
int deleteCount = db.delete(SimpleSQLiteHelper.DATABASE_TABLE,
selection, selectionArgs);
// 通知所有的观察者,数据集已经改变
getContext().getContentResolver().notifyChange(uri, null);
return deleteCount;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// 打开一个可写的数据库连接
SQLiteDatabase db = sqLiteHelper.getWritableDatabase();
// 如果是行 URI,限定删除行为指定的行
switch (uriMatcher.match(uri)) {
case SINGLE_ROW:
String rowID = uri.getPathSegments().get(1);
selection = SimpleSQLiteHelper.KEY_ID
+ " = "
+ rowID
+ (!TextUtils.isEmpty(selection) ? " AND (" + selection
+ ")" : "");
break;
default:
break;
}
// 执行更新
int updateCount = db.update(SimpleSQLiteHelper.DATABASE_TABLE, values,
selection, selectionArgs);
// 通知所有的观察者,数据集已经改变
getContext().getContentResolver().notifyChange(uri, null);
return updateCount;
}
}
Content Resolver
可以通过 Content Resolver 来访问 Content Provider 提供的数据。可以通过 getContext().getContentResolver()
方法来获取一个 Content Resolver 对象。
监听 Content Provider 中的数据变化
如果 共享的数据发生变化,可以在 Content Provider 发生数据变化时调用 getCotentResolver().notifyChange(uri, null)
来通知注册在此 URI 上的访问者
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。