Android提供了应用程序之间相互访问的统一接口,这些接口被定义在Content Provider中,其中包括添加、删除、修改和查询等操作。
一、Content Provider简介
Content Provider用来保存和检索数据,并且使应用程序之间相互访问数据成为可能,它是跨应用程序共享数据的唯一方法。
Android为常用的数据类型(如音视频、图片和联系方式等)提供了大量的Content Provider,它们被定义在android.provider包下,通过这样定义好的Content Provider我们可以方便的进行数据操作。当然我们必须拥有适当的权限。
1、Content Provider的常用方法
我们定义一个Content Provider必须实现以下几个抽象方法:
- query(Uri,String[],String,String[],String)查询
- insert(Uri,ContentValues)插入
- update(Uri,ContentValues,String,String[])更新
- delete(Uri,String,String[])删除
- getType(Uri)获得MIME数据类型
有关Content Provider的常用方法如下:
方法名称 | 方法描述 |
insert(Uri,ContentValues) | 插入 |
delete(Uri,String,String[]) | 删除 |
update(Uri,ContentValues,String,String[]) | 更新 |
query(Uri,String[],String,String[],String) | 查询 |
getType(Uri) | 获得MIME数据类型 |
onCreate() | 当ContentProvider创建时调用 |
getContext() | 获得Context对象 |
2、ContentResolver
我们是在ContentProvider中实现我们实际操作数据的方法的。但是,客户端调用时,我们用到了另外一个接口,它就是ContentResolver。ContentReslover中提供和ContentProvider中对应的方法。我们是间接的通过操作ContentResolver来操作ContentProvider的。
ContentResolver通过应用程序的getContentResolver()方法获得。一般情况下,ContentResolver是单例的,但是可以雨哦多个ContentResolver在不同的应用程序和不同的进程之间和ContentProvider交互。
3、URI
ContentProvider是通过URI对象来共享其数据的。
一个URI对象必须以“content://”开头,接下来是URI的授权部分,这部分内容要和AndroidManifest.xml配置文件中声明的授权内容一致,后面还可能有数据类型和记录ID。
通过URI可以使得ContentResolver知道和哪个ContentProvider对应,并且来操作哪些表以及哪些记录。
完整的一个URI如图所示:
content://com.example.transportationprovider/trains/122
4、查询系统ContentProvider内容
1)、通过对应getContentResolver()方法,获得ContentResolver对象
2)、获得ContentResolver的URI标示
3)、列出想要查询的列
4)、调用ContentResolver的query方法执行查询
如下代码实现了对系统联系人的有条件查询
private void query(){
//获得ContentResolver实例
ContentResolver cr = getContentResolver();
//定义URI
Uri uri = Contacts.People.CONTENT_URI;
//定义查询数组
String[] projection = {Contacts.PeopleColumns.NAME,Contacts.PeopleColumns.NOTES};
//查询条件
String selection = Contacts.PeopleColumns.NAME + "=?";
//查询条件参数
String[] selectionArgs = {"tom"};
//排序列
String sortOrder = Contacts.PeopleColumns.NAME;
//查询获得游标
Cursor c = cr.query(uri, projection, selection, selectionArgs, sortOrder);
//判断游标是否为空
if(c.moveToFirst()){
//遍历游标
for (int i = 0; i < c.getCount(); i++) {
c.moveToPosition(i);
//获得列索引
int idx = c.getColumnIndexOrThrow(PeopleColumns.NAME);
//输入日志,列值
Log.i("Test", c.getString(idx));
}
}
}
5、添加系统ContentProvider内容
1)、通过对应的getContentResolver()方法获得ContentResolver对象
2)、获得ContentResolver的URI表示
3)、将添加的信息封装到ContentValues对象中
4)、调用ContentResolver对象的insert方法执行添加
如下代码添加了联系人姓名和备注列信息
public void insert(){
//获得ContentResolver实例
ContentResolver cr = getContentResolver();
//实例化contentValues
ContentValues values = new ContentValues();
//定义URI
Uri uri = Contacts.People.CONTENT_URI;
//添加姓名
values.put(People.NAME, "tom");
//添加备注
values.put(People.NOTES, "I'm tom!");
//插入数据
cr.insert(uri, values);
}
6、添加系统ContentProvider图片内容
在android.content中提供了MediaStoreContentProvider类,该类用来实现一些多媒体文件的操作,例如,图片音频、视频文件等。
如下代码实现了添加一张图片的功能
public void insert2(){
//实例化ContentValues
ContentValues values = new ContentValues(3);
//添加图片名称、描述和类型
values.put(Media.DISPLAY_NAME, "Girl");
values.put(Media.DESCRIPTION, "Beauty");
values.put(Media.MIME_TYPE, "image/jpeg");
//插入数据
Uri uri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, values);
//获得Bitmap实例
Bitmap sourceBitmap = BitmapFactory.decodeFile("/data/data/mx.android.ch10/files/girl.jpg");
//定义输出流
OutputStream outStream;
try {
//获得输出流
outStream = getContentResolver().openOutputStream(uri);
//压缩输出流
sourceBitmap.compress(Bitmap.CompressFormat.JPEG, 50, outStream);
outStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
二、自定义ContentProvider
1、创建ContentProvider的步骤
要使用ContentProvider操作数据,必须要有保存数据的场所。可以使用文件或SQLite数据库的方式来保存数据,通常使用SQLite数据库。
为了访问数据库,就要提供访问数据库的接口。这里需要基础ContentProvider类,实现其中访问数据库的抽象方法,这些方法有:
- query():查询
- insert():插入
- update():更新
- delete():删除
- getType():获得类型
- onCreate():创建时调用
另外,定义好的ContentProvider必须在AndroidManifest.xml配置文件中声明才能使用。声明中必须添加的参数是授权属性“android:authorities”。
综上所述,创建ContentProvider的步骤如下:
1)、创建保存数据的文件或数据库
2)、定义一个类继承ContentProvider,实现抽象方法
3)、将定义好的ContentProvider在配置文件中声明,以便使用
2、ContentProvider实例
以下实例演示的是员工信息维护,包括添加、删除、修改和查询员工信息。这里员工信息比较简单,只定义编号、姓名、性别和年龄。
1)、常量类
/**
* 常量类,该类主要是用来声明一些常量,如列名称、排序列、URI和内容类型等。
* 该类有一个内部类Employee实现BaseColumns接口,BaseColumns接口是一个常量接口,
* 里面有一个_id和_count字符串常量,分别用来表示记录ID和记录总数
*/
public final class Employees {
//授权常量
public static final String AUTHORITY = "mx.android.ch10.Employees";
private Employees(){}
//内部类
public static final class Employee implements BaseColumns{
//构造方法
private Employee(){}
//访问Uri
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/employee");
//内容类型
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.mx.employees";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.mx.employees";
//默认排序常量
public static final String DEFAULT_SORT_ORDER = "name DESC";//按姓名排序
//表字段常量
public static final String NAME = "name";//姓名
public static final String GENDER = "gender";//性别
public static final String AGE = "age";//年龄
}
}
2)、数据库帮助类
/**
* 数据库帮助类
* 在其中声明了数据库名称、数据库版本和表名称一些常量
*/
public class DBHelper extends SQLiteOpenHelper {
//数据库常量名称
public static final String DATABASE_NAME = "Employees.db";
//数据库版本常量
public static final int DATABASE_VERSION = 1;
//表名称常量
public static final String EMPLOYEES_TABLE_NAME = "employee";
//构造方法
public DBHelper(Context context) {
//创建数据库
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
//创建时调用
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table " + EMPLOYEES_TABLE_NAME + " ("
+ Employee._ID + " integer primary key,"
+ Employee.NAME + " text,"
+ Employee.GENDER + " text,"
+ Employee.AGE + " integer"
+ ");");
}
//版本更新时调用
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//删除表
db.execSQL("drop table if exists employee");
onCreate(db);
}
}
3)、EmployeeProvider类
/**
* 该类操作数据库
* 引用DBHelper类来获得实例,引用UriMatcher工具类来对URI进行匹配比较,引用HashMap保存查询列
* 在onCreate方法中实例化DBHelper获得数据库实例
* 在insert方法中实现插入数据,在deleted方法中实现删除数据,在query方法中实现查询数据,在update方法中实现更新数据
*/
public class EmployeeProvider extends ContentProvider {
//数据库帮助类
private DBHelper dbHelper;
//Uri工具类
private static final UriMatcher sUriMatcher;
//查询、更新条件
private static final int EMPLOYEE = 1;
private static final int EMPLOYEE_ID = 2;
//查询列集合
private static HashMap<String,String> empProjectionMap;
static{
//Uri匹配工具类
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(Employees.AUTHORITY, "employee", EMPLOYEE);
sUriMatcher.addURI(Employees.AUTHORITY, "employee/#",EMPLOYEE_ID);
//实例化查询列集合
empProjectionMap = new HashMap<String, String>();
//添加查询列
empProjectionMap.put(Employee._ID,Employee._ID);
empProjectionMap.put(Employee.NAME, Employee.NAME);
empProjectionMap.put(Employee.GENDER, Employee.GENDER);
empProjectionMap.put(Employee.AGE, Employee.AGE);
}
//创建时调用
@Override
public boolean onCreate() {
dbHelper = new DBHelper(getContext());
return true;
}
//添加方法
@Override
public Uri insert(Uri uri, ContentValues values) {
//获得数据库实例
SQLiteDatabase db = dbHelper.getWritableDatabase();
//插入数据,返回行Id
long rowId = db.insert(DBHelper.EMPLOYEES_TABLE_NAME, Employee.NAME, values);
//如果插入成功,返回Uri
if(rowId > 0){
Uri empUri = ContentUris.withAppendedId(Employee.CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(empUri, null);
return empUri;
}
return null;
}
//删除方法
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
//获得数据库实例
SQLiteDatabase db = dbHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)) {
//根据指定条件删除
case EMPLOYEE:
count = db.delete(DBHelper.EMPLOYEES_TABLE_NAME, selection, selectionArgs);
break;
//根据指定条件和Id删除
case EMPLOYEE_ID:
String noteId = uri.getPathSegments().get(1);
count = db.delete(DBHelper.EMPLOYEES_TABLE_NAME, Employee._ID + "="
+ noteId + (!TextUtils.isEmpty(selection)?" and (" + selection + ')' : ""), selectionArgs);
break;
default:
throw new IllegalArgumentException("错误的URI" + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
//获得类型
@Override
public String getType(Uri uri) {
return null;
}
//查询方法
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
switch (sUriMatcher.match(uri)) {
//查询所有
case EMPLOYEE:
qb.setTables(DBHelper.EMPLOYEES_TABLE_NAME);
qb.setProjectionMap(empProjectionMap);
break;
//根据ID查询
case EMPLOYEE_ID:
qb.setTables(DBHelper.EMPLOYEES_TABLE_NAME);
qb.setProjectionMap(empProjectionMap);
qb.appendWhere(Employee._ID + "=" + uri.getPathSegments().get(1));
break;
default:
throw new IllegalArgumentException("Uri错误" + uri);
}
//使用默认排序
String orderBy;
if(TextUtils.isEmpty(sortOrder)){
orderBy = Employee.DEFAULT_SORT_ORDER;
}else{
orderBy = sortOrder;
}
//获得数据库实例
SQLiteDatabase db = dbHelper.getReadableDatabase();
//返回游标集合
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
//更新方法
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
//获得数据库实例
SQLiteDatabase db = dbHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)) {
//根据指定条件更新
case EMPLOYEE:
count = db.update(dbHelper.EMPLOYEES_TABLE_NAME, values, selection, selectionArgs);
break;
//根据指定条件和Id更新
case EMPLOYEE_ID:
String noteId = uri.getPathSegments().get(1);
count = db.update(dbHelper.EMPLOYEES_TABLE_NAME, values, Employee._ID + "="
+ noteId + (!TextUtils.isEmpty(selection)?" and (" + selection + ')' : ""), selectionArgs);
break;
default:
throw new IllegalArgumentException("Uri错误" + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
}
4)、AndroidManifest.xml
<provider android:name="EmployeeProvider" android:authorities="mx.android.ch10.Employees" />
5)、Activity
/**
* 测试
*/
public class EmployeeActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//添加
insert();
//查询
query();
//更新
update();
//查询
query();
//删除
delete();
//查询
query();
}
//删除方法
private void delete(){
//删除Id为1的记录
Uri uri = ContentUris.withAppendedId(Employee.CONTENT_URI, 1);
//获得ContentResolver,并删除
getContentResolver().delete(uri, null, null);
}
//更新
private void update(){
//更新Id为1的记录
Uri uri = ContentUris.withAppendedId(Employee.CONTENT_URI,1);
ContentValues values = new ContentValues();
//添加员工信息
values.put(Employee.NAME, "hz.guo");
values.put(Employee.GENDER, "male");
values.put(Employee.AGE, 31);
getContentResolver().update(uri, values, null, null);
}
//查询
private void query(){
//查询列数组
String[] PROJECTION = new String[]{
Employee._ID, //0
Employee.NAME, //1
Employee.GENDER, //2
Employee.AGE //3
};
//查询所有备忘录信息
Cursor c = managedQuery(Employee.CONTENT_URI, PROJECTION, null, null, Employee.DEFAULT_SORT_ORDER);
if(c.moveToFirst()){
for (int i = 0; i < c.getCount(); i++) {
c.moveToPosition(i);
String name = c.getString(1);
String gender = c.getString(2);
int age = c.getInt(3);
Log.i("emp",name + ":" + gender + ":" + age);
}
}
}
//插入
private void insert(){
Uri uri = Employee.CONTENT_URI;
ContentValues values = new ContentValues();
values.put(Employee.NAME, "mixnue");
values.put(Employee.GENDER, "male");
values.put(Employee.AGE, 20);
getContentResolver().insert(uri, values);
}
}