安卓的内容提供器是四大组件之一,提供了一种跨应用程序访问数据的方式,这包含两方面:(1)在自己的应用程序中访问其他应用程序的数据库(例如通过系统提供的内容提供器访问联系人、短信、图片等)。(2)构建自己的ContentProvider开放自己的数据库给其他的应用程序(或者自己应用程序的访问)。
和内容提供器相关有三个重要的类ContentResolver,ContentProvider与ContentObserver。
1. ContentResolver
ContentResolver可以通过Context的getContentResolver()方法取得实例,每个应用程序在创建时都会提供一个ContentResolver,ContentResolver相当于一个代理,通过ContentResolver来对内容提供器进行操作,他提供了CURD方法,对应着数据库中的CURD操作。
ContentResolver通过Uri来匹配具体的访问数据表。内容提供器中的Uri由三部分组成,
第一部分是内容提供器的标识content://,第二部分是authority,用来区分要访问数据库所属的应用程序,第三部分是path,标识着要访问的数据表或具体的那条数据。因此一个标准的Uri写法如下
content://com.example.app.provider/tablename
一 二 三
*符号表示匹配任意长度的任意字符
符号表示匹配任意长度的数字
content://com.example.app.provider/*
表示访问该应用程序内的任意一张表
content://com.example.app.provider/tablname/#
表示访问tablename表中的任意一行
ContentResolver提供query()、insert()、update()、delete()用来查询、增加、更新、删除数据。
query()方法查询数据:
Cursor cursor=getContentResolver().query(uri,projection,selection,selectionArgs,sortOrder);
uri指明查询的路径,projection指定要查询的列,selection与 selectionArgs指定查询的条件(如Sql中 “where id > 10”),sortOrder指定查询结果排序方式。
insert()方法插入数据:
ContentValues values=new ContentValues();
value.put("name","jack");
value.put("age",10);
getContentResolver.insert(uri,values);
update()方法更新数据:
ContentValues values=new ContentValues();
values.put("name","macro");
getContentResovler().update(uri,values,"name= ? and age=? " , new String[] {" jack","10"});
delete()方法删除数据:
getContentResolver.delete(uri,"name = ?" , new String[] {"macro"});
2. 创建内容提供器ContentProvider
自定义内容提供器需要重写6个抽象方法:onCreate(),getType(),query(),insert(),update(),delete(),
一个基本的自定义内容提供器框架如下
public class MyProvider extends ContentProvider {
public static final int TABLE_DIR=0;
public static final int TABLE_ITEM=1;
private static UriMatcher uriMatcher;
static
{
uriMatcher=new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI("com.example.app.provider","tablename",TABLE_DIR);
uriMatcher.addURI("com.example.app.provider","tablename/#",TABLE_ITEM);
}
@Override
public boolean onCreate() {
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
switch (uriMatcher.match(uri))
{
case TABLE_DIR:
return "vnd.android.cursor.dir/vnd.com.example.app.provider.tablename";
break;
case TABLE_ITEM:
return "vnd.android.cursor.item/vnd.com.example.app.provider.tablename";
break;
default:
break;
}
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
//这里填写查询逻辑
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
//这里填写插入逻辑
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
//这里填写更新逻辑
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
//这里填写删除逻辑
}
}
TABLE_DIR ,TABLE_ITEM为定义的两个常量,分别表示整个表和表中Item。
UriMatcher是一个uri匹配器,可以自己设置authority , path 与code( int 类型) 的对应关系,例如uriMatcher.addURI("com.example.app.provider","tablename",TABLE_DIR)就建立了 authority=com.example.app.provider ,path=tablename 与TABLE_DIR 之间的关系,通过uriMather.match("content://com.example.app.provider/tablename")就会返回TABLE_DIR。
当ContentResolver尝试访问数据时候,内容提供器就会被初始化,onCreate()方法执行,返回值代码是否初始化成功。这里往往添加一些数据库的创建升级等操作(例如获取DatabaseHelper实例 dbHelper=new MyDatabaseHelper(。。。) )。
query()、insert()、update()、delete()中用来处理根据提供的uri与其他参数来操作数据库的实际逻辑,例如查询数据库、插入数据等。
getType()方法会根据Uri返回对应MIME类型。MIME字符串也由三部分组成:
1.必须以vnd开头
2.如果URI以表名结尾,vnd后面接android.cursor.dir,以表中某一id(item)结果,vnd后面接android.cursor.item。
3.然后在最后接上vnd.<authority>.<path>。
例如
vnd.android.cursor.dir/vnd.com.example.app.provider.table
表示访问表
vnd.android.cursor.item/vnd.com.example.app.provider.table
表示访问表中某一item
内容提供器是四大组件之一,因此需要在AndroidManifest.xml文件中配置一下。
<provider
android:name=".MyProvider"
android:authorities="com.example.app.provider"
android:enable="true"
android:exported="true"
>
</provider>
enable表示是否启用这个内容提供器,exported表示是否允许其他应用程序访问这个内容提供器。
3. ContentObserver内容监听者
某些时候数据库发生改变(例如新建了联系人,从网络刷新了新数据、或者来短信了),我们不能及时知道就导致已有的数据成为旧数据,然而通过query()方法再去查询需要主要去操作或者另起线程定期查询这样效率低而且很被动,这时候就可以使用ContentObserver,ContentObserver使用观察者模式,当数据库改变时候通知注册的observer实例对象执行更新操作。
创建自己的内容观察者对象:
private ContentObserver mContentObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange, Uri uri) {
//这里执行更新逻辑;
}
}
};
继承ContentObserver类需要重写onChange()方法,当数据库发生改变时候会回调这个onChange()方法。声明ContentObserver实例对象时候需要传入一个Handler()对象。
通过ContentResolver来注册和取消注册这个观察者
//注册观测者 第一个参数为监听的uri, 第二个参数表示是否应用到子Uri,如果为true表示对派生Uri依然有效。
getContentResolver().registerContentObserver(uri, true, mContentObserver);
//注销观察者
getContentResolver().unregisterContentObserver(mContentObserver);