1.为什么要用ContentProvider
ContentProvider是安卓四大组件之一,它的作用就是用来在应用程序之间提供一个传递数据的接口。
数据库在Android当中是私有的,当然这些数据包括文件数据和数据库数据以及一些其他类型的数据。
不能将数据库设为WORLD_READABLE,每个数据库都只能创建它的包访问,这意味着只有由创建数据库的进程可访问它,被各自的应用程序所占有。
如果需要在进程间传递数据,则可以使用AIDL/Binder或创建一个ContentProvider,但是不能跨越进程/包边界直接来使用数据库。
2.ContentProvider的作用
一个Content Provider类实现了一组标准的方法接口,从而能够让其他的应用保存或读取此Content Provider的各种数据类型。
也就是说,一个程序可以通过实现一个Content Provider的抽象接口将自己的数据暴露出去。
外界根本看不到,也不用看到这个应用暴露的数据在应用当中是如何存储的,或者是用数据库存储还是用文件存储,还是通过网上获得,这些一切都不重要,
重要的是外界可以通过这一套标准及统一的接口和程序里的数据打交道,可以读取程序的数据,也可以删除程序的数据
当然,中间也会涉及一些权限的问题。
继承了ContentProvider的类需要实现如下方法:
· query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder):通过Uri进行查询,返回一个Cursor。
· insert(Uri url, ContentValues values):将一组数据插入到Uri 指定的地方。
· update(Uri uri, ContentValues values, String where, String[] selectionArgs):更新Uri指定位置的数据。
· delete(Uri url, String where, String[] selectionArgs):删除指定Uri并且符合一定条件的数据。
3.ContentResolver的操作外界的程序通过ContentResolver接口可以访问ContentProvider提供的数据,在Activity当中通过getContentResolver()可以得到当前应用的 ContentResolver实例。
ContentResolver提供的接口和ContentProvider中需要实现的接口对应,主要有以下几个。
· query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder):通过Uri进行查询,返回一个Cursor。
· insert(Uri url, ContentValues values):将一组数据插入到Uri 指定的地方。
· update(Uri uri, ContentValues values, String where, String[] selectionArgs):更新Uri指定位置的数据。
· delete(Uri url, String where, String[] selectionArgs):删除指定Uri并且符合一定条件的数据。
4.实例说明
下面用一个实例来说明ContentProvider的用法
本应用程序提供了一个ContentProvider的接口,同时也实现了ContentResolver来通过该接口来存储信息。也就是应用程序内部进行存取。
数据存储的方法我们用的是本地数据库SQLite,当然也可以用比如文件存储的方法等等。
首先必须定义一个SQLiteOpenHelper对象,可以编写一个类来继承它
定义如下:
package ycitss.sqlite3.db;
import ycitss.cp.FirstProviderMetaData;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
public class DatabaseHelper extends SQLiteOpenHelper {
private static final int VERSION = 1;
public DatabaseHelper(Context context, String name, CursorFactory factory,
int version) {
super(context, name, factory, version);
}
public DatabaseHelper(Context context, String name, int version) {
this(context, name, null, version);
}
public DatabaseHelper(Context context, String name) {
this(context, name, VERSION);
}
public void onCreate(SQLiteDatabase db) {
System.out.println("create a Database");
db.execSQL("create table " + FirstProviderMetaData.USERS_TABLE_NAME
+ "(" + FirstProviderMetaData.UserTableMetaData._ID
+ " INTEGER_PARIMARY_AUTOINCREMENT,"
+ FirstProviderMetaData.UserTableMetaData.USER_NAME
+ " varchar(20))");
}
public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) {
System.out.println("update a Database");
}
}
该类实现了onCreate和onUpgrade方法,也是必须实现的方法。其中onCreate方法,里面包含了一个Sql语句,新建了一个表user。在事先的构造方法里接受了数据库的名字版本等信息创建了数据库。这个类可以作为一个工具类。
主要功能就是 用构造方法创好数据库,然后用onCreate方法创建一张表用于存储信息。
import android.net.Uri;
import android.provider.BaseColumns;
public class FirstProviderMetaData {
//注册时的权限变量,为本应用程序注册一个ContentProvider
public static final String AUTHORITY = "ycitss.cp.FirstContentProvider";
//数据库的名字,传入DatabaseHelper中来在构造方法里面创建数据库
public static final String DATABASE_NAME = "FirstProvider.db";
//数据库的版本号
public static final int DATABASE_VERSION = 1;
//表的名字
public static final String USERS_TABLE_NAME = "users";
//实现BaseColumns接口,里面包含了一个id变量所以不用再次定义
public static final class UserTableMetaData implements BaseColumns {
public static final String TABLE_NAME = "users";
//访问数据时要用到的Uri
public static final Uri CONTENT_URI = Uri.parse("content://"
+ AUTHORITY + "/users");
//两个数据访问类型,一个是访问整张表内容,一个是访问表中某一元素
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.firstprovider.user";
public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/vnd.firstprovider.user";
//一个列名
public static final String USER_NAME = "name";
//默认排序方式
public static final String DEFAULT_SORT_ORDER = "_id desc";
}
}
下面是整个ContentProvider类的实现,实现了插入和查询的功能
import java.util.HashMap;
import ycitss.cp.FirstProviderMetaData.UserTableMetaData;
import ycitss.sqlite3.db.DatabaseHelper;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
public class FirstContentProvider extends ContentProvider {
// UriMatcher用来匹配Uri的类型
public static final UriMatcher uriMatcher;
public static final int INCOMING_USER_COLLECTION = 1;
public static final int INCOMING_USER_SINGLE = 2;
private DatabaseHelper dh;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//向匹配器中加入两个Uri
uriMatcher.addURI(FirstProviderMetaData.AUTHORITY, "users",
INCOMING_USER_COLLECTION);
uriMatcher.addURI(FirstProviderMetaData.AUTHORITY, "users/#",
INCOMING_USER_SINGLE);
}
public static HashMap userProjectionMap;
static {
userProjectionMap = new HashMap();
userProjectionMap.put(UserTableMetaData._ID, UserTableMetaData._ID);
userProjectionMap.put(UserTableMetaData.USER_NAME,
UserTableMetaData.USER_NAME);
}
public int delete(Uri arg0, String arg1, String[] arg2) {
//方法未实现
System.out.println("delete...");
return 0;
}
//获取Uri的类型,判断是要获取整张表内容还是表中一个元素
public String getType(Uri uri) {
System.out.println("getType...");
switch (uriMatcher.match(uri)) {
case INCOMING_USER_COLLECTION:
return UserTableMetaData.CONTENT_TYPE;
case INCOMING_USER_SINGLE:
return UserTableMetaData.CONTENT_TYPE_ITEM;
default:
throw new IllegalArgumentException("Unknow URI " + uri);
}
}
/**
* ContentProvider提供的插入方法,返回插入数据的Uri
* 应用程序用ContentResolver调用insert方法可以得到Uri
* content://ycitss.cp.firstContentProivder/users/1
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
System.out.println("insert...");
//得到可写入类型的数据库
SQLiteDatabase db = dh.getWritableDatabase();
long rowId = db.insert(UserTableMetaData.TABLE_NAME, null, values);
if (rowId > 0) {
//新生成的rowId追加到CONTENT_URI后面
Uri insertedUserUri = ContentUris.withAppendedId(
UserTableMetaData.CONTENT_URI, rowId);
// 通知监听器,数据已经改变
getContext().getContentResolver().notifyChange(insertedUserUri,
null);
return insertedUserUri;
}
throw new SQLException("Failed to insert row into " + uri);
}
//新生成ContentProvider时调用,自动调用
@Override
public boolean onCreate() {
dh = new DatabaseHelper(getContext(),
FirstProviderMetaData.DATABASE_NAME);
System.out.println("onCreate...");
return true;
}
/*
* ContentProvider提供的查询方法,返回一个Cursor对象,必须接收Uri才能实现查询
* 由ContentResolver调用query传入Uri来调用
* */
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
//进行Uri的匹配
switch (uriMatcher.match(uri)) {
//查询整张表
case INCOMING_USER_COLLECTION:
qb.setTables(UserTableMetaData.TABLE_NAME);
qb.setProjectionMap(userProjectionMap);
break;
//查询单个元素
case INCOMING_USER_SINGLE:
qb.setTables(UserTableMetaData.TABLE_NAME);
qb.setProjectionMap(userProjectionMap);
qb.appendWhere(UserTableMetaData._ID + "="
+ uri.getPathSegments().get(1));
break;
}
String orderBy;
if (TextUtils.isEmpty(sortOrder)) {
orderBy = UserTableMetaData.DEFAULT_SORT_ORDER;
} else {
orderBy = sortOrder;
}
SQLiteDatabase db = dh.getWritableDatabase();
Cursor c = qb.query(db, projection, selection, selectionArgs, null,
null, orderBy);
c.setNotificationUri(getContext().getContentResolver(), uri);
System.out.println("query...");
return c;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
//方法还未实现
System.out.println("update...");
return 0;
}
}
下面是应用程序测试Activity
package ycitss.cp;
import ycitss.cp.FirstProviderMetaData.UserTableMetaData;
import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class CPActivity extends Activity {
/** Called when the activity is first created. */
private Button insertButton = null;
private Button queryButton = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
insertButton = (Button) findViewById(R.id.insertButton);
insertButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ContentValues values = new ContentValues();
values.put(FirstProviderMetaData.UserTableMetaData.USER_NAME,
"zhangsan");
//传入ContentUri和values键值对,将数据插入
Uri uri = getContentResolver().insert(
FirstProviderMetaData.UserTableMetaData.CONTENT_URI,
values);
System.out.println("uri ---> " + uri.toString());
}
});
queryButton = (Button) findViewById(R.id.queryButton);
queryButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Cursor c = getContentResolver().query(
FirstProviderMetaData.UserTableMetaData.CONTENT_URI,
null, null, null, null);
//遍历输出
while (c.moveToNext()) {
System.out.println(c.getString(c
.getColumnIndex(UserTableMetaData.USER_NAME)));
}
}
});
}
}
另外,必须对ContentProvider进行注册,这样系统方法context.getContentResolver()才能找到我们重写的ContentProvider。在注
册ContentProvider时,采用了authorities(主机名/域名)的方法对它进行唯一标识,ContentProvider的标识类似于网站的域名,通过此域名我们可以
准确访问到对应网站,网站为我们提供数据。Authorities就是ContentProvider的域名。
提供ContentProvider的应用程序应在AndroidMainFest.xml文件中写入如下代码,代码在application标签之内,和Activity标签属于同一等级
这样,就可以通过调用getContent().getContentResolver()方法获得的ContentResolver对象对ContentProvider进行操作了。
欢迎留言交流!