共享应用程序内的数据, 在数据修改时可以监听

1、特点

①、可以将应用中的数据对外进行共享;

②、数据访问方式统一,不必针对不同数据类型采取不同的访问策略;

③、内容提供者将数据封装,只暴露出我们希望提供给其他程序的数据(这点有点类似Javabeans);

④、内容提供者中数据更改可被监听;

2、创建内容提供者

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

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

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

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

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

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

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

l  name:             为全类名;

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

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

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

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

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

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

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

  • 操作 URI的UriMather与ContentUris工具类:

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

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

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

  1. withAppendedId(uri, id)  为路径加上ID部分;
  2. 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

 

3、内容解析者ContentResolver

Ÿ  通过Context获得ContentResolver内容访问者对象(内容提供者的解析器对象);

Ÿ  调用ContentResolver对象的方法即可访问内容提供者

测试类代码:

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")));

}

}

 

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

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

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

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

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

通过Context的getContentResolver()方法获取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();

}

});

}

 

5、区别Provider/Resolver/Observer

1)ContentProvider:内容提供者

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

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

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

2)ContentResolver:内容解析者

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

3)ContentObserver:内容观察者

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

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

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

ContentPriver的更多相关文章

  1. SQLite数据库与Contentprovider(2)

    ContentProvider: 在创建ContentProvider时,需要首先使用数据库.文件系统或网络实现底层存储功能, 然后在继承ContentProvider的类中实现基本数据操作的接口函数 ...

随机推荐

  1. iOS状态栏详解(隐藏)

    状态栏的隐藏 状态栏的隐藏主要有两种方法:方法一:通过代码控制 @interface UIApplication(UIApplicationDeprecated) // Setting statusB ...

  2. Android Studio使用心得

    说实话 開始接触这个工具 真的认为非常恶心 毕竟大陆被墙  非常多东西用起来不是非常方便 并且Eclipse转到Android Studio还是一个跨度 废话不多说  以下 讲下我遇到的问题 1. 安 ...

  3. C语言 · 芯片测试

    基础练习 芯片测试   时间限制:1.0s   内存限制:512.0MB    问题描述 有n(2≤n≤20)块芯片,有好有坏,已知好芯片比坏芯片多. 每个芯片都能用来测试其他芯片.用好芯片测试其他芯 ...

  4. UTF-8以字节为单位对Unicode进行编码

    UTF-8以字节为单位对Unicode进行编码.从Unicode到UTF-8的编码方式如下: Unicode编码(16进制) UTF-8 字节流(二进制) 000000 - 00007F 0xxxxx ...

  5. Tslib步骤以及出现问题的解决方案(转)

    嵌入式设备中触摸屏使用非常广泛,但触摸屏的坐标和屏的坐标是不对称的,需要校准.校准广泛使用的是开源的tslib. Tslib是一个开源的程序,能够为触摸屏驱动获得的采样提供诸如滤波.去抖.校准等功能, ...

  6. Hibernate使用createSqlQuery进行模糊查询时找不到数据

    1. 首先明确一点,使用createSqlQuery如下两种方式的占位符都可以使用,这个在官方的文档可以查到. 注意使用模糊查询时,赋值两边不可以添加单引号. Query query = sess.c ...

  7. Dropwizard与Spring Boot比较

    在这篇文章中我们将讨论的Java轻量级框架Dropwizard和Spring Boot的相似性和差异. 首先,这是一个选择自由和速度需要,无论你在Dropwizard和Spring Boot选择哪个, ...

  8. ORA-12504 warning in PHP

    <?php $conn = oci_connect('proekt', 'proekt1', 'localhost:1521'); $stid = oci_parse($conn, " ...

  9. MySQL5.7远程连接和增加密码

    主要是5.7的很多操作和以前版本不一样,所以踩了很多坑. 1. 远程连接cant connect to mysql (10061) 一开始以为是权限问题,所以参考了详解 MySQL 5.7 新的权限与 ...

  10. NetBeans启动Tomcat报“'127.0.0.1' 不是内部或外部命令”启动失败的解决办法

    http://blog.sina.com.cn/s/blog_709548200102vgy4.html ——————————————————————————————————————————————— ...