Android笔记(ContentProvider)
一、ContentProvider的数据模型和URI
数据模型:
ContentProvider 使用基于数据库模型的简单表格来提供需要共享的数据,在该表格中,每一行表示一条记录,而每一列代表特定类型和含义的数据,并且其中每一条数据记录都包含一个名为“_ID”的字段类标识每条数据。
Uri:
如果B应用程序想操纵A应用程序的数据,那么B应用程序一般通过ContentResolver类操纵A应用程序的数据,在ContentResolver类中,通过URI标识B程序要操纵A程序中的哪个数据。Uri为内容提供者中的数据建立了唯一标识符。它主要由三部分组成,scheme、authorities和path。
其中,authority部分指定了B程序要操纵的目标应用程序(即A程序),通常用目标应用程序的包名进行命名。path部分主要是对目标应用程序中不同的表做区分。
对uri做补充:
譬如现在A程序中有两张表table1和table2,A的包名为cn.itcast.mycontentprovider,B程序想要操纵table1这张表,uri则为:
content:// cn.itcast.mycontentprovider/table1
想要操纵table2这张表,uri则为:
content:// cn.itcast.mycontentprovider/table2
如果B程序希望访问A程序table1表中id为1的数据,那么还可以在URI的最后加/id号,例如content:// cn.itcast.mycontentprovider/table1/1
二、访问其他程序中的数据方法
通过ContentProvider查询其他程序数据的具体步骤如下:
1. 通过parse()方法解析Uri
例如:Uri uri = Uri.parse("content://cn.itcast.mycontentprovider/person");
2. 通过query()方法查询数据
//获取ContentResolver对象
ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder);
3. 通过while()循环语句遍历查询到的数据
while (cursor.moveToNext()) {
String address = cursor.getString(0);
long date = cursor.getLong(1);
int type = cursor.getInt(2);
}
cursor.close(); //关闭cursor
对目标应用程序中的数据进行增添数据,删除数据,修改数据的方法与查询数据类似,主要是2,3两步有所区别,但无一例外,都是要先获得uri对象,再获取ContentResolver对象,最后通过ContentResolver对象中的insert,update, delete等方法进行操作。
三、创建自己的内容提供器
考虑这样一个场景,仍然是B程序想操纵A程序中的数据,A程序现在由我们自己编写,那么A程序就需要用内容提供器来将自己的数据暴露。那么如何创建自己的内容提供器呢?创建步骤见书6.2或者ppt,ppt上写的很详细。
创建好的内容提供者的默认代码如下:
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
public class MyContentProvider2 extends ContentProvider {
public MyContentProvider2() {
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// Implement this to handle requests to delete one or more rows.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public String getType(Uri uri) {
// TODO: Implement this to handle requests for the MIME type of the data
// at the given URI.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO: Implement this to handle requests to insert a new row.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public boolean onCreate() {
// TODO: Implement this to initialize your content provider on startup.
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO: Implement this to handle query requests from clients.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO: Implement this to handle requests to update one or more rows.
throw new UnsupportedOperationException("Not yet implemented");
}
}
其中,创建出来的内容提供器继承自ContentProvider这个类,ContentProvider这个类有6个抽象方法。创建出来的内容提供器需要重写这6个方法。6个方法的简介如下:
onCreate(): 初始化内容提供器时调用,在这里通常完成数据库的创建和升级。只有当ContentResolver尝试访问程序中的数据时,内容提供器才会被初始化。
query(): 从内容提供器中查询数据
insert(): 向内容提供器中添加数据
update(): 更新内容提供器中已有的数据
delete(): 从内容提供器中删除数据
getType(): 根据传入的内容URI返回相应的MIME类型
有关ContentProvider在AndroidManifest.xml中的注册以及相关属性,见教材p129-130。那么如何具体地重写内容提供器?
1. UriMatcher类
譬如A程序中有3个表table1,table2,table3,B程序想操纵A程序中的数据,就需要对不同的表做区分,之前讲过是用uri标识数据,那么就需要用不同的uri做区分,此时就可以用UriMatcher类对Uri进行匹配。具体步骤见书131页或者ppt。
2. addURI方法
addURI方法将需要的Uri注册到UriMatcher中,希望注册的uri的格式主要有以下几种。
匹配一张表:content:// cn.itcast.mycontentprovider/table1
匹配一条数据:content:// cn.itcast.mycontentprovider/table1/1
还可以使用通配符*和#
*表示匹配任意长度的字符,#表示匹配任意长度的数字
匹配任意表:content:// cn.itcast.mycontentprovider/*
匹配任意一行数据:content:// cn.itcast.mycontentprovider/table1/#
addURI主要有三个参数,分别是authority,path和自定义的代码,其中,authoriy和path就指定了要匹配的uri样式,自定义的代码就是匹配成功时的返回值。根据这个代码,就可以判断出调用方期望访问的是哪张表中的数据了。
3. getType()方法
该方法是所有的内容提供器必须提供的一个方法,用于获取uri对象对应的MIME类型。MIME字符串由3部分组成
- 必须vnd开头
- 如果uri以路径结尾(即表示匹配的是一张表),后接android.cursor.dir/,如果以id结尾(即匹配的是一条记录),后接android.cursor.item/
- 最后接vnd.<authority>.<path>
在A应用程序中写完ContentProvider后,便可新建应用程序B,从而访问A应用程序中的数据
完整示范代码:
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
public class MyContentProvider extends ContentProvider {
public static final int BOOK_DIR = 0;
public static final int BOOK_ITEM = 1;
public static final int CATEGORY_DIR = 2;
public static final int CATEGORY_ITEM = 3;
public static final String AUTHORITY = "com.example.a30228.dct2application";
//定义一个uriMatcher,这个uriMatcher用来匹配其他应用程序请求的uri
private static UriMatcher uriMatcher;
private MyOpenHelper dbhelper;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//注册需要的uri(本质上来讲就是将数据库当中特定数据暴露出去)
uriMatcher.addURI(AUTHORITY, "Book", BOOK_DIR);
uriMatcher.addURI(AUTHORITY, "Book/#", BOOK_ITEM);
uriMatcher.addURI(AUTHORITY, "Category", CATEGORY_DIR);
uriMatcher.addURI(AUTHORITY, "Category/#", CATEGORY_ITEM);
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// Implement this to handle requests to delete one or more rows.
SQLiteDatabase db = dbhelper.getReadableDatabase();
int deleteRows = 0;
switch(uriMatcher.match(uri)){
case BOOK_DIR: //表示其他应用程序的uri和注册的uri当中Book表匹配成功
deleteRows = db.delete("Book",selection, selectionArgs);
break;
case BOOK_ITEM: //表示其他应用程序的uri和注册的uri当中Book表中某条记录匹配成功
String bookId = uri.getPathSegments().get(1);
deleteRows = db.delete("Book","id=?", new String[]{bookId});
break;
case CATEGORY_DIR:
deleteRows = db.delete("Category",selection, selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
deleteRows = db.delete("Category","id=?", new String[]{categoryId});
break;
}
//db.close();
return deleteRows;
}
@Override
public String getType(Uri uri) {
// TODO: Implement this to handle requests for the MIME type of the data
// at the given URI.
switch (uriMatcher.match(uri)){
case BOOK_DIR:
return "vnd.android.cursor.dir/vnd.com.example.a30228.dct2application.Book";
case BOOK_ITEM:
return "vnd.android.cursor.item/vnd.com.example.a30228.dct2application.Book";
case CATEGORY_DIR:
return "vnd.android.cursor.dir/vnd.com.example.a30228.dct2application.Category";
case CATEGORY_ITEM:
return "vnd.android.cursor.item/vnd.com.example.a30228.dct2application.Category";
}
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO: Implement this to handle requests to insert a new row.
SQLiteDatabase db = dbhelper.getReadableDatabase();
Uri uriReturn = null;
switch(uriMatcher.match(uri)){
case BOOK_DIR:
case BOOK_ITEM:
long newBookId = db.insert("Book", null, values);
uriReturn = Uri.parse("content://"+AUTHORITY+"/Book/"+newBookId);
break;
case CATEGORY_DIR:
case CATEGORY_ITEM:
long newCategoryId = db.insert("Category", null, values);
uriReturn = Uri.parse("content://"+AUTHORITY+"/Category/"+newCategoryId);
break;
}
//db.close();
return uriReturn;
}
@Override
public boolean onCreate() {
// TODO: Implement this to initialize your content provider on startup.
dbhelper = new MyOpenHelper(getContext());
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO: Implement this to handle query requests from clients.
SQLiteDatabase db = dbhelper.getReadableDatabase();
Cursor cursor = null;
switch(uriMatcher.match(uri)){
case BOOK_DIR:
cursor = db.query("Book",projection, selection, selectionArgs,
null, null, sortOrder);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
cursor = db.query("Book",projection, "id=?", new String[]{bookId},
null, null, sortOrder);
break;
case CATEGORY_DIR:
cursor = db.query("Category",projection, selection, selectionArgs,
null, null, sortOrder);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
cursor = db.query("Category",projection, "id=?", new String[]{categoryId},
null, null, sortOrder);
break;
}
//db.close();
return cursor;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO: Implement this to handle requests to update one or more rows.
SQLiteDatabase db = dbhelper.getReadableDatabase();
int updateRows = 0;
switch (uriMatcher.match(uri)){
case BOOK_DIR:
updateRows = db.update("Book", values, selection, selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
updateRows = db.update("Book", values, "id=?", new String[]
{bookId});
break;
case CATEGORY_DIR:
updateRows = db.update("Category", values, selection, selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
updateRows = db.update("Category", values, "id=?", new String[]
{categoryId});
break;
}
//db.close();
return updateRows;
}
}