一、首先要明白,ContentProvider(以下简称为CP)是什么。
1,是一套数据存储和获取的统一接口。
2,最大的特点是,可以在不同的应用程序间共享数据。
3,Android系统自身已经提供数个CP,包括音频、视频、通讯录的数据。
4,如第一条所说,CP是一套数据存储和获取的接口。所以有不同的实现,其中最常见的是使用SQLite的实现,数据被保存在一张表中。
5,CP需要向外部提供一个唯一的Uri(统一资源标识符),来标明资源的位置。该Uri的格式为:
[color=red]content://[/color]xxx.yyy.zzz[color=blue]/aaa[/color]其中红色部分为统一的,表示协议、
xxx.yyy.zzz被称为authority,是一个唯一的字符串,按doc推荐,通常采用项目的包名、
蓝色部分/aaa被称为PathSegments,表示该资源更确切的位置,是一个目录结构。某些时候,可以将其理解为一张表。
二、自定义CP不是很有必要,除非应用需要向外部共享数据。一般都是使用Android系统提供的CP。
三、自定义CP的实现方法。
此处采用常见的SQLite方式实现。
1,书写一个类,继承android.content.ContentProvider,该类是抽象类,需要实现以下的方法:
[*]boolean onCreate():很明显,该方法作为CP创建时由系统调用的方法,通常执行初始化操作。
[*]Uri insert(Uri uri, ContentValues values):插入数据的方法,返回最新插入数据的Uri。
[*]int delete(Uri uri, String selection, String[] selectionArgs):删除数据的方法,返回操作影响行数。
[*]int update(Uri uri, ContentValues values, String selection,String[] selectionArgs):更新数据的方法,返回操作影响行数。
[*]Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder):查询数据的方法,返回包含数据的游标对象。
[*]String getType(Uri uri):取得该uri的MIME类型,返回表示MIME类型的字符串。
从以上方法的参数可见,确实用SQLite的相关API来实现CP是很合适的。
2,自定义CP必要的内容。
[*]authority:确定了该CP的唯一性,Uri的必要组成部分,以常量的形式定义。同时需要在AndroidManifest.xml中进行配置。
eg:
[*]uri:该CP的资源定位,以常量的形式定义。一个CP允许有多个Uri,以PathSegment进行区分和定位。
eg:
最后的字符串location_1即为PathSegment。
[*]需要一个定义一个android.content.UriMatcher常量。在静态块中进行初始化。
eg:
[*]以字符串常量的形式定义该CP拥有的MIME类型,通常需要定义两个,表示单条数据的MIME类型和复数条数据的MIME类型。
eg:
vnd.android.cursor.item:固定的,表示单条数据。
vnd.android.cursor.dir:固定的,表示复数数据。
“/”之后为自定义,可随意。
[*]一个静态常量Map<String,String>,用于描述数据存储的结构,可以理解为描述表结构。即有哪些列和列的别名。
[color=red]此段内容在下在学习中也不甚懂,不知有什么必要,此处只姑且写出通常推荐的作法。[/color]
3,几个重点方法的实现。
[*]String getType(Uri uri)
此方法主要的作用在于根据uri返回该uri所对应的数据MIME类型。
个人认为,该方法的作用在于,在查询方法中判断是查询单条数据还是多条数据以作不同处理。如果是查询单条数据,即可从uri中取出所要查询数据的id
[*]Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder),查询方法
[*]Uri insert(Uri uri, ContentValues values),插入数据方法。
4,Activity中使用CP。
首先需要在AndroidManifest.xml中对自定义CP进行注册,
eg:
自定义CP在应用启动时,由系统实例化(调用CP的onCreate方法,并且会实例化所有注册了的CP),在Activity中通过ContentResolver来操作自定义CP。
而对于查询,则调用Activty的mangedQuery方法。
或者ContentResolver的query方法。
1,是一套数据存储和获取的统一接口。
2,最大的特点是,可以在不同的应用程序间共享数据。
3,Android系统自身已经提供数个CP,包括音频、视频、通讯录的数据。
4,如第一条所说,CP是一套数据存储和获取的接口。所以有不同的实现,其中最常见的是使用SQLite的实现,数据被保存在一张表中。
5,CP需要向外部提供一个唯一的Uri(统一资源标识符),来标明资源的位置。该Uri的格式为:
[color=red]content://[/color]xxx.yyy.zzz[color=blue]/aaa[/color]其中红色部分为统一的,表示协议、
xxx.yyy.zzz被称为authority,是一个唯一的字符串,按doc推荐,通常采用项目的包名、
蓝色部分/aaa被称为PathSegments,表示该资源更确切的位置,是一个目录结构。某些时候,可以将其理解为一张表。
二、自定义CP不是很有必要,除非应用需要向外部共享数据。一般都是使用Android系统提供的CP。
三、自定义CP的实现方法。
此处采用常见的SQLite方式实现。
1,书写一个类,继承android.content.ContentProvider,该类是抽象类,需要实现以下的方法:
[*]boolean onCreate():很明显,该方法作为CP创建时由系统调用的方法,通常执行初始化操作。
[*]Uri insert(Uri uri, ContentValues values):插入数据的方法,返回最新插入数据的Uri。
[*]int delete(Uri uri, String selection, String[] selectionArgs):删除数据的方法,返回操作影响行数。
[*]int update(Uri uri, ContentValues values, String selection,String[] selectionArgs):更新数据的方法,返回操作影响行数。
[*]Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder):查询数据的方法,返回包含数据的游标对象。
[*]String getType(Uri uri):取得该uri的MIME类型,返回表示MIME类型的字符串。
从以上方法的参数可见,确实用SQLite的相关API来实现CP是很合适的。
2,自定义CP必要的内容。
[*]authority:确定了该CP的唯一性,Uri的必要组成部分,以常量的形式定义。同时需要在AndroidManifest.xml中进行配置。
eg:
public static final String AUTHORITY = "org.ashtray.todolist";
[*]uri:该CP的资源定位,以常量的形式定义。一个CP允许有多个Uri,以PathSegment进行区分和定位。
eg:
public static finalUri URI_1 = Uri.parse("content://" + AUTORITY + "/location_1");
最后的字符串location_1即为PathSegment。
[*]需要一个定义一个android.content.UriMatcher常量。在静态块中进行初始化。
eg:
public static final UriMatcher matcher = null;
public static final int TODOS = 0;
public static final int TODO = 1;
static{
matcher = new UriMatcher(UriMatcher.NO_MATCH);
//为matcher添加映射,用于过滤uri,
//todolist,todolist/#为uri的形式,
//TODOS,TODO未定义的两个整型常量,用于标识该类型的uri
matcher.addURI(AUTHORTY, "todolist", TODOS);
matcher.addURI(AUTHORTY, "todolist/#", TODO);
}
[*]以字符串常量的形式定义该CP拥有的MIME类型,通常需要定义两个,表示单条数据的MIME类型和复数条数据的MIME类型。
eg:
public static final String CONTENT_ITEMS_TYPE= "vnd.android.cursor.dir/vnd.ashtray.todolist";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.ashtray.todolist";
vnd.android.cursor.item:固定的,表示单条数据。
vnd.android.cursor.dir:固定的,表示复数数据。
“/”之后为自定义,可随意。
[*]一个静态常量Map<String,String>,用于描述数据存储的结构,可以理解为描述表结构。即有哪些列和列的别名。
[color=red]此段内容在下在学习中也不甚懂,不知有什么必要,此处只姑且写出通常推荐的作法。[/color]
public static final Map<String,String> projectionMap = null;
static {
projectionMap = new HashMap<String,String>();
projectionMap.put("col_1","col_1");
projectionMap.put("col_2","col_2");
projectionMap.put("col_3","col_3");
}
3,几个重点方法的实现。
[*]String getType(Uri uri)
此方法主要的作用在于根据uri返回该uri所对应的数据MIME类型。
@Override
public String getType(Uri uri) {
//UriMatcher对象的match方法,获取uri对应的int型标识
//该映射关系需要用首先用matcher.addURI进行添加
switch (matcher.match(uri)) {
case Const.TODO:
//返回uri对应的MIME类型
return Const.CONTENT_ITEM_TYPE;
case Const.TODOS:
return Const.CONTENT_ITEMS_TYPE;
default:
return null;
}
}
个人认为,该方法的作用在于,在查询方法中判断是查询单条数据还是多条数据以作不同处理。如果是查询单条数据,即可从uri中取出所要查询数据的id
//以List<String>的形式返回authority之后的内容,以“/”隔开
String id = uri.getPathSegments().get(1)
[*]Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder),查询方法
@Override
public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder)
{
//通过SQLiteOpenHelper的实现类,获取数据库对象
SQLiteDatabase db = helper.getReadableDatabase();
//根据uri取得查询内容的MIME类型
String mime = this.getType(uri);
//创建查询
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
//设置描述数据结构的Map
qb.setProjectionMap(getProjection(projection));
//如果MIME类型为查询单条数据,则从uri中取出数据的id,并添 加为查询的条件
if(Const.CONTENT_ITEM_TYPE.equalsIgnoreCase(mime)){
qb.appendWhere(Const.COL_ID + "=" + uri.getPathSegments().get(1));
}
//设置查询的表
qb.setTables(Const.TABLE_NAME);
//执行查询,并返回Cursor对象
Cursor cursor = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder);
//设置更新通知,以获取最新的数据
cursor.setNotificationUri(this.getContext().getContentResolver(), uri);
return cursor;
}
[*]Uri insert(Uri uri, ContentValues values),插入数据方法。
SQLiteDatabase db = this.helper.getWritableDatabase();
long id = db.insert(Const.TABLE_NAME, null, values);
if(id < 0){
throw new SQLException("insert field");
}
//将插入数据的id添加到Uri并返回
Uri curr = ContentUris.withAppendedId(Const.TODO_LIST_URI, id);
//将新插入的数据uri进行通知,以便获取最新的数据
this.getContext().getContentResolver().notifyChange(curr, null);
return curr;
4,Activity中使用CP。
首先需要在AndroidManifest.xml中对自定义CP进行注册,
eg:
<provider
<!--自定义CP的类全名-->
android:name="org.ashtray.cp.TodoListContentProvider"
<!--authority,自定义CP的唯一标识,与代码中定义的字符串相同-->
android:authorities="org.ashtray.todolist"/>
自定义CP在应用启动时,由系统实例化(调用CP的onCreate方法,并且会实例化所有注册了的CP),在Activity中通过ContentResolver来操作自定义CP。
而对于查询,则调用Activty的mangedQuery方法。
或者ContentResolver的query方法。