第十章:Content Provider

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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值