Android ContentProvider

一、ContentProvider介绍

1、ContentProvider:为存储和获取数据提供统一的接口,可以在不同的应用程序之间共享数据。 
2、Android内置的许多数据都是使用ContentProvider形式,供开发者调用的 (如视频,音频,图片,通讯录等)。 
3、虽然使用其他方法也可以对外共享数据,但数据访问方式会因数据存储的方式而不同。 
- 采用文件方式对外共享数据,需要进行文件操作读写数据; 
- 采用sharedpreferences共享数据,需要使用sharedpreferences API读写数据。 
- 使用ContentProvider共享数据的好处是统一了数据访问方式。


二、自定义ContentProvider

1、定义类继承ContentProvider,根据需要重写其内容方法(6个方法):

onCreate()创建内容提供者时,会调用这个方法,完成一些初始化操作;

crud相应的4个方法用于对外提供CRUD操作;

getType()返回当前Url所代表数据的MIME类型:

返回的是单条记录:以vnd.android.cursor.item/ 开头,如:vnd.android.cursor.item/person

返回的是多条记录:以vnd.android.cursor.dir/ 开头,如:vnd.android.cursor.dir/person

2、在清单文件的<application>节点下进行配置,<provider>标签中需要指定name、authorities、exported属性

name:为全类名;

authorities:是访问Provider时的路径,要唯一;

exported:用于指示该服务是否能够被其他应用程序组件调用或跟它交互

3、URI代表要操作的数据,由scheme、authorites、path三部分组成:

content://com.itheima.sqlite.provider/person

scheme:固定为content,代表访问内容提供者;

authorites:<provider>节点中的authorites属性;

path:程序定义的路径,可根据业务逻辑定义;

4、操作 URI的UriMather与ContentUris工具类:

当程序调用CRUD方法时会传入Uri

UriMatcher:表示URI匹配器,可用于添加Uri匹配模式,与匹配Uri(见下代码);

ContentUris:用于操作Uri路径后面的ID部分,2个重要的方法:

(1)withAppendedId(uri, id)为路径加上ID部分;

(2)parseId(uri) 用于从路径中获取ID部分;

5、定义类继承ContentProvider,根据需要重写其内容方法(6个方法):

onCreate()创建内容提供者时,会调用这个方法,完成一些初始化操作;

crud相应的4个方法用于对外提供CRUD操作;

getType()返回当前Url所代表数据的MIME类型:

返回的是单条记录:以vnd.android.cursor.item/ 开头,如:vnd.android.cursor.item/person

返回的是多条记录:以vnd.android.cursor.dir/ 开头,如:vnd.android.cursor.dir/person

6、在清单文provider>标签中需要指定name、authorities、exported属性件的<application>节点下进行配置,

name:为全类名;

authorities:是访问Provider时的路径,要唯一;

exported:用于指示该服务是否能够被其他应用程序组件调用或跟它交互

7、URI代表要操作的数据,由scheme、authorites、path三部分组成:

content://com.itheima.sqlite.provider/person

scheme:固定为content,代表访问内容提供者;

authorites:<provider>节点中的authorites属性;

path:程序定义的路径,可根据业务逻辑定义;

8、操作 URI的UriMather与ContentUris工具类:

当程序调用CRUD方法时会传入Uri

UriMatcher:表示URI匹配器,可用于添加Uri匹配模式,与匹配Uri(见下代码);

ContentUris:用于操作Uri路径后面的ID部分,2个重要的方法:

9. withAppendedId(uri, id)为路径加上ID部分;

10. parseId(uri) 用于从路径中获取ID部分;

 

public class HeimaProvider extends ContentProvider {
	private static final int PERSON = 1;			// 匹配码
	private static final int STUDENT = 2;			// 匹配码
	private static final int PERSON_ID = 3;		// 匹配码
	private MyHelper helper;
	/** Uri匹配器 */
	private UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
	@Override
	public boolean onCreate() {
		System.out.println("onCreate...");
		helper = new MyHelper(getContext());
		// == 添加 uri 匹配模式, 设置匹配码(参数3) Uri如果匹配就会返回相应的匹配码 ==
		uriMatcher.addURI("com.itheima.sqlite.provider", "person", PERSON);						  
		uriMatcher.addURI("com.itheima.sqlite.provider", "#", PERSON_ID);				// #表示匹配数字,*表示匹配文本
		uriMatcher.addURI("com.itheima.sqlite.provider", "student", STUDENT);
		return true;
	}
	@Override
	public Uri insert(Uri uri, ContentValues values) {
		SQLiteDatabase db = helper.getWritableDatabase();
		switch (uriMatcher.match(uri)) {						// 匹配uri
		case PERSON:
			long id = db.insert("person", "id", values);
			db.close();
			return ContentUris.withAppendedId(uri, id);			// 在原uri上拼上id,生成新的uri并返回;	
		case STUDENT:
			long insert = db.insert("student", "id", values);
			System.out.println("数据文件中,没有student表,也不会报错");
			db.close();
			return ContentUris.withAppendedId(uri, insert);		// 为路径上,加上ID
		default:
			throw new IllegalArgumentException(String.format("Uri:%s 不是合法的uri地址", uri));
		}
	}
	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		SQLiteDatabase db = helper.getWritableDatabase();
		switch (uriMatcher.match(uri)) {							// 匹配uri
		case PERSON_ID:
			long parseId = ContentUris.parseId(uri);				// 获取传过来的ID值
			selection = "id=?";									// 设置查询条件
			selectionArgs = new String[] { parseId + "" };			// 查询条件值
		case PERSON:
			int delete = db.delete("person", selection, selectionArgs);
			db.close();
			return delete;
		default:
			throw new IllegalArgumentException(String.format("Uri:%s 不是合法的uri地址", uri));
		}
	}
	@Override
	public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
		SQLiteDatabase db = helper.getWritableDatabase();
		switch (uriMatcher.match(uri)) {
		case PERSON_ID:
			long parseId = ContentUris.parseId(uri);				// 获取传过来的ID值
			selection = "id=?";									// 设置查询条件
			selectionArgs = new String[] { parseId + "" };			// 查询条件值
		case PERSON:
			int update = db.update("person", values, selection, selectionArgs);
			db.close();
			return update;
		default:
			throw new IllegalArgumentException(String.format("Uri:%s 不是合法的uri地址", uri));
		}
	}
	@Override
	public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
		SQLiteDatabase db = helper.getWritableDatabase();
		switch (uriMatcher.match(uri)) {
		case PERSON_ID:
			// == 根据ID查询 ==
			long parseId = ContentUris.parseId(uri);				// 获取传过来的ID值
			selection = "id=?";									// 设置查询条件
			selectionArgs = new String[] { parseId + "" };			// 查询条件值
		case PERSON:
			Cursor cursor = db.query("person", projection, selection, selectionArgs, null, null, sortOrder);
			// == 注意:此处的 db与cursor不能关闭 ==
			return cursor;
		default:
			throw new IllegalArgumentException(String.format("Uri:%s 不是合法的uri地址", uri));
		}
	}
	// 返回传入URI的类型,可用于测试URI是否正确
	@Override
	public String getType(Uri uri) {
		switch (uriMatcher.match(uri)) {
		case PERSON_ID:
			return "vnd.android.cursor.item/person";		// 表示单条person记录
		case PERSON:
			return "vnd.android.cursor.dir/person";		// 表单多个person记录
		default:
			return null;
		}
	}
}


<provider
     android:exported="true"
     android:name="com.itheima.sqlite.provider.HeimaProvider"
     android:authorities="com.itheima.sqlite.provider" />

authorities 可以配置成如下形式(系统联系人的):

android:authorities="contacts;com.android.contacts"

“;” 表示的是可使用 contacts,com.android.contacts

 

、监听内容提供者的数据变化

1、在内容提供者中可以通知其他程序数据发生变化

通过Context的getContentResolver()方法获取ContentProvider

调用其notifyChange()方法发送数据修改通知,发送到系统的公共内存(消息信箱中)

2、在其他程序中可以通过ContentObserver监听数据变化

通过Context的getContentResolver()方法获取ContentResolver

调用其registerContentObserver()方法指定对某个Uri注册ContentObserver

自定义ContentObserver,重写onChange()方法获取数据

 

public class HeimaProviderTest extends AndroidTestCase {
	/** 测试添加数据 */
	public void testInsert() {
		ContentResolver resolver = this.getContext().getContentResolver();
		Uri uri = Uri.parse("content://com.itheima.sqlite.provider/person");
		ContentValues values = new ContentValues();
		values.put("name", "小翼");
		values.put("balance", 13000);
		Uri insert = resolver.insert(uri, values);			// 获取返回的uri,如:content://com.itheima.sqlite.provider/7
		System.out.println(insert);
	}
	/** 测试删除 */
	public void testRemove() {
		ContentResolver resolver = this.getContext().getContentResolver();
		Uri uri = Uri.parse("content://com.itheima.sqlite.provider/person");
		int count = resolver.delete(uri, "id=?", new String[] { 3 + "" });
		System.out.println("删除了" + count + "行");
	}
	/** 测试更新 */
	public void testUpdate() {
		ContentResolver resolver = this.getContext().getContentResolver();
		Uri uri = Uri.parse("content://com.itheima.sqlite.provider/person");
		ContentValues values = new ContentValues();
		values.put("name", "小赵 update");
		values.put("balance", 56789);
		int update = resolver.update(uri, values, "id=?", new String[] { 6 + "" });
		System.out.println("更新了" + update + "行");
	}
	/** 测试查询 */
	public void testQueryOne() {
		ContentResolver resolver = this.getContext().getContentResolver();
		Uri uri = Uri.parse("content://com.itheima.sqlite.provider/person");
		Cursor c = resolver.query(uri, new String[] { "name", "balance" }, "id=?", new String[] { 101 + "" }, null);
		if (c.moveToNext()) {
			System.out.print(c.getString(0));
			System.out.println(" " + c.getInt(1));
		}
		c.close();
	}
	/**测试查询全部 */
	public void testQueryAll() {
		ContentResolver resolver = this.getContext().getContentResolver();
		Uri uri = Uri.parse("content://com.itheima.sqlite.provider/person");
		Cursor c = resolver.query(uri, new String[] { "id", "name", "balance" }, null, null, "name desc");
		while (c.moveToNext()) {
			System.out.println(c.getInt(0) + ", " + c.getString(1) + ", " + c.getInt(2));
		}
		c.close();
	}
	/** 测试查询一条 */
	public void testQueryOneWithUriId() {
		ContentResolver resolver = this.getContext().getContentResolver();
		Uri uri = Uri.parse("content://com.itheima.sqlite.provider/3");		// 查询ID为3的记录
		Cursor c = resolver.query(uri, new String[] { "id", "name", "balance" }, null, null, null);
		if (c.moveToNext()) {
			System.out.println(c.getInt(0) + ", " + c.getString(1) + ", " + c.getInt(2));
		}
		c.close();
	}
	/** 测试获取内容提供者的返回类型 */
	public void testGetType() {
		ContentResolver resolver = this.getContext().getContentResolver();
		System.out.println(resolver.getType(Uri.parse("content://com.itheima.sqlite.provider/2")));
		System.out.println(resolver.getType(Uri.parse("content://com.itheima.sqlite.provider/person")));
	}
}

四、监听内容提供者的数据变化

1、在内容提供者中可以通知其他程序数据发生变化

通过ContextgetContentResolver()方法获取ContentResolver

       调用其notifyChange()方法发送数据修改通知,发送到系统的公共内存(消息信箱中)

2、在其他程序中可以通过ContentObserver监听数据变化

通过ContextgetContentResolver()方法获取ContentResolver

调用其registerContentObserver()方法指定对某个Uri注册ContentObserver

自定义ContentObserver,重写onChange()方法获取数据

示例代码(发通知部分):
public int delete(Uri uri, String selection, String[] selectionArgs) {
	SQLiteDatabase db = helper.getWritableDatabase();
	int delete = db.delete("person", selection, selectionArgs);
	// == 通过内容访问者对象ContentResolve 发通知给所有的Observer ==
	getContext().getContentResolver().notifyChange(uri, null);
	db.close();
	return delete;
}
监听部分:
// 注册内容观察者事件
private void initRegisterContentObserver() {
	Uri uri = Uri.parse("content://com.itheima.sqlite.provider");	// 监听的URI
	// == 第2个参数:true表示监听的uri的后代都可以监听到 ==
	getContentResolver().registerContentObserver(uri, true, new ContentObserver(new Handler()) {
		public void onChange(boolean selfChange) {			// 接到通知就执行 								
			personList = personDao.queryAll();														
			((BaseAdapter) personListView.getAdapter()).notifyDataSetChanged();
		}
	});
}

五、Provider/Resolver/Observer区别

1)ContentProvider:内容提供者

把一个应用程序的私有数据(如数据库)信息暴露给别的应用程序,让别的应用程序可以访问;

在数据库中有对应的增删改查的方法,如果要让别的应用程序访问,需要有一个路径uri:

通过content:// 路径对外暴露,uri写法:content://主机名/表名

2)ContentResolver:内容解析者

根据内容提供者的路径,对数据进行操作(crud);

3)ContentObserver:内容观察者

可以理解成android系统包装好的回调,数据发送变化时,会执行回调中的方法;

ContentResolver发送通知,ContentObserver监听通知;

A的数据发生变化的时候,A就会显示的通知一个内容观察者,不指定观察者,就会发消息给一个路径

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值