Android学习笔记2-1--组件2--Provider

来源:互联网 发布:淘宝活动时间表 编辑:程序博客网 时间:2024/06/11 15:59

组件简介


ContentProvider将程序内部数据向其他程序公开。ContentResolver对程序内部数据进行CRUD操作。需要通过ContentResolver对象传递URI间接调用ContentProvider。

通过ContentResolver访问ContentProvider。在发起的一个请求的过程中,Android系统根据URI确定处理这个查询的ContentProvider,然后初始化ContentProvider所有需要的资源(这个初始化的工作是Android系统完成)。一般情况下只有一个ContentProvider对象,但却可以同时与多个ContentResolver进行交互。

ContentProvider完全屏蔽了底层数据源的数据存储方法。数据提供者通过ContentProvider提供了一组标准的数据操作接口,但却无须知道数据提供者的内部数据的存储方法。数据提供者可以使用SQLite数据库存储数据,也可以通过文件系统或SharedPreferences存储数据,甚至是使用网络存储的方法,这些数据的存储方法和存储设备对数据使用者都是不可见的。正是这种屏蔽模式很大程度上简化了ContentProvider的使用方法,使用者只要调用ContentProvider提供的接口函数即可完成所有的数据操作,而数据存储方法则是ContentProvider设计者需要考虑的问题。

ContentProvider的数据集类似于数据库的数据表。每行是一条记录且每列具有相同的数据类型,每条记录都包含一个长整型的字段_ID用来唯一标识每条记录。ContentProvider可以提供多个数据集,调用者使用URI对不同数据集的数据进行操作。

URI是一个用于标识某一互联网资源名称的字符串。URI标识允许用户对任何(包括本地和互联网)的资源通过特定的协议进行交互操作。ContentProvider使用的URI语法结构如下:

content://<authority>/<data_path>/<id># 例如:content://cn.hisuites.andr.test.xxx.provider/people/girl/3# content://是通用前缀,表示该UIR用于ContentProvider定位资源。# <authority>是授权名称,用来确定具体由哪一个ContentProvider提供资源。# <data_path>是数据路径,用来确定请求的是哪个数据集。数据集的数据路径可以写成多段格式,如people/girl和people/boy。如果ContentProvider仅提供一个数据集,数据路径则可以省略;如果ContentProvider提供多个数据集,数据路径必须指明具体数据集。# <id>是数据编号,用来唯一确定数据集中的一条记录,匹配数据集中_ID字段的值。如果请求的数据不只一条,<id>可以省略。

组件定义

DEMO看这里。

组件声明

# 实现类为TProvider# 授权者名称为cn.hisuites.andr.test.xxx.provider的ContentProvider# exported的true为可被外部程序访问<provider android:name=".model.TProvider"          android:authorities="cn.hisuites.andr.test.xxx.provider"           android:exported="true"/>

组件方法

ContentProvider也是继承ComponentCallbacks2,有些方法参考Contenxt。

# 一般用来初始化底层数据集和建立数据连接等工作。true表示初始化成功,false表示初始化失败public boolean onCreate()# 用来返回指定URI的MIME数据类型。# 若URI是单条数据,则返回的MIME数据类型以vnd.android.cursor.item开头。# 若URI是多条数据,则返回的MIME数据类型以vnd.android.cursor.dir/开头。public String getType(...)# 用于对数据集的增删改查操作,注意,在正确操作获取结果后有个结尾public Uri insert(...)public int delete(...)public int update(...)public Cursor query(...# 结尾getContext().getContentResolver().notifyChange(uri, null);

在正确操作后有个结尾,也可以看本文后面的Observer。

辅助工具

UriMatcher

UriMatcher本质上是一个文本过滤器,用在contentProvider中帮助我们过滤,分辨出查询者想要查询哪个数据表。

static{      # 构造函数中,UriMatcher.NO_MATCH是URI无匹配时的返回代码值为-1      uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);      # addURI方法用来添加新的匹配项      uriMatcher.addURI(AUTHORITY, PATH_MULTIPLE, MULTIPLE_PEOPLE);      uriMatcher.addURI(AUTHORITY, PATH_SINGLE, SINGLE_PEOPLE);}# authority表示匹配的授权者名称,path表示数据路径(#代表任何数字),code表示返回代码public void addURI(String authority, String path, int code);# 在getType中使用例子:@Overridepublic String getType(Uri uri) {    switch(uriMatcher.match(uri)){        case MULTIPLE_PEOPLE:             //多条数据的处理             break;        case SINGLE_PEOPLE:             //单条数据的处理             break;        default:             throw new IllegalArgumentException("不支持的URI:" + uri);    }}

ContentUris

ContentUris是content URI的一个辅助类。它有两个方法很有用,具体如下所示。

# 把content URI后边的id解析出来public static long parseId(Uri contentUri)# 把id和contentUri连接成一个新的Uripublic static Uri withAppendedId(Uri contentUri, long id)

组件使用

通过ContentResolver访问ContentProvider。每个Android组件都有一个ContentResolver对象,通过调用getContentResolver()方法可得到ContentResolver对象。

# 准备ContentResolver resolver = getContentResolver();String CONTENT_URI = "xxxxxxx";String KEY_ID = "_id";String KEY_NAME = "name";String KEY_AGE = "age";String KEY_HEIGHT = "height";# 增加:通过insert()函数添加单条数据ContentValues values = new ContentValues();values.put(KEY_NAME, "Tom");values.put(KEY_AGE, 21);values.put(KEY_HEIGHT, 1.81f);Uri newUri = resolver.insert(CONTENT_URI, values);# 添加:通过bulkInsert()函数添加多条数据ContentValues[] arrayValues = new ContentValues[10];    //实例化每一个ContentValues...int count = resolver.bulkInsert(CONTENT_URI, arrayValues );# 删除:通过delete语句删除一条或多条数据Uri uri = Uri.delete(CONTENT_URI + "/" +"2");String deleteVal = null;int result = resolver.delete(uri, deleteVal, null);# 删除:通过selection语句删除一条或多条数据Uri uriVal = Uri.parse(CONTENT_URI);String deleteVal = KEY_ID + ">4";int result = resolver.delete(uriVal, deleteVal, null);# 修改Uri uriVal = Uri.parse(CONTENT_URI + "/" + "2");ContentValues updateVal = new ContentValues();values.put(KEY_NAME, "Tom");values.put(KEY_AGE, 21);values.put(KEY_HEIGHT, 1.81f);int result = resolver.update(uriVal, updateVal, null, null);# 查询Uri uriVal = Uri.parse(CONTENT_URI + "/" + "2");String[] queryVal = new String[]{KEY_ID, KEY_NAME, KEY_AGE, KEY_HEIGHT};Cursor cursor = resolver.query(uriVal, queryVal, null, null, null);# 在URI中定义了需要查询数据的ID后,在query()函数中没有必要再加入其他的查询条件。# 如果要获取数据集全部数据,则可以直接使用CONTENT_URI且不加查询条件。 

ContentProvider的查询结果的返回值是数据集的指针Cursor类。在提取Cursor数据中的数据前,推荐测试Cursor中的数据数量,避免在数据获取中产生异常。

int resultCounts = cursor.getCount();if(resultCounts == 0 !cursor.moveToFirst()){    return null;}//...

如何监听数据变化

不管是ContentProvider中实现的方法中的任何一个,程序都会调用getContext().getContentResolver().notifyChange(uri,null);这行代码可用于通知所有注册在该Uri上的监听者该ContentProvider所共享的数据发生了改变。本质是通过ContentResolver为ContentProvider注册一个ContentObserver。

方法是调用ContentResolver的registerContentObserver方法为其注册个ContentObserver。当它所监听的ContentProvider所共享的数据发生改变时,该onChange将会触发。

getContentResolver().registerContentObserver(Uri uri,boolean notifyForDescendents,ContentObserver observer)# notifyForDescendents参数:# 如果参数为false,那么只有content://abc的数据改变时会触发该监听器。# 如果该参数设为true,假如Uri为content://abc,那么Uri为content://abc/xyz, content://abc/xyz/foo的数据改变时也会触发该监听器。

请注意:ContentResolver运行在主线程中。ContentResolver并不是一个新的线程,也不是新的进程。也就是说,若您需要在ContentResolver中执行较为耗时的操作(如播放音乐、执行网络请求等),需要在ContentResolver中创建一个新的线程。这可以防止ANR的发生,同时主线程可以执行正常的UI操作。

也可通过Cursor.setNotificationUri间接的注册一个ContentObserver,具体的后续再看。

最后,可以看看这篇文章,很不错。

原创粉丝点击