Android ContentProvider介绍
在Android中数据的存储一共有五种形式,分别是:Shared Preferences、网络存储、文件存储,外储存储、SQLite。但是我们知道一般这些存储都只是在单独的一个应用程序之中达到一个数据的共享。而使用ContentProvider共享数据的好处是统一了数据访问方式。ContentProvide对数据进行封装,不用关心数据存储的细节。下面我们通过代码来介绍这个ContentProvider的使用过程。
public class ItemContentProvier {/*Authority*/ public static final String AUTHORITY="com.itchq.contentprovider";/*Match Code*/public static final int ITEM=1;public static final int ITEM_POS=2;/*Default sort order*/public static final String DEFAULT_SORT_ORDER = "_id asc";/*MIME*/ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.com.itchq.contentprovider"; public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.com.itchq.contentprovider";public static final String ARTICE_ITEM="articeitem";/*Content URI*/public static final Uri CONTENT_ARTICE=Uri.parse("content://"+AUTHORITY+"/"+ARTICE_ITEM);public interface Artice{public static final String ID="_id";public static final String TITLE="title";public static final String CONTENT_ID="contentid";}}
上面这个类都是定义数据库一些常量,我们往往把它单独的提前出来使代码结构更清晰一点。
URI 通用资源标志符,这个Uri代表了要操作的数据,后面要访问这个数据库就可以通过这个URI来指定,用于唯一标识某个具体的ContentProvider
URI格式如下:
[content://][com.itchq.contentprovider][/artice][/1]
|------A------|-----------------B-------------------|---C---|---D--|
A:前缀表明数据受控于一个内容提供者。它从不修改,也就是schema,ContentProvider(内容提供者)的scheme已经由Android所规定, scheme为:content://
B:称为Authority,它唯一地标识了一个特定的Content Provider,因此,这部分内容一般使用Content Provider所在的package来命名,使得它是唯一的,在上面的代码中使用 public static final String AUTHORITY="com.itchq.contentprovider";定义这个变量,这个还有一个地方使用到,就是在AndroidMainfest.xml中要声明这个数据库的时候需要使用,如下代码:
<providerandroid:name="com.itchq.contentprovider.itContentProvider"android:authorities="com.itchq.contentprovider"/>
上面这个android:authorities这个就是和我们的AUTHORITY里面定义的字符串是一样的,这个地方要注意点,如果不一样外部就不可以访问这个数据库了,我们所说的URI唯一性也是在这个地方来体现出来的,
C:称为资源路径,它表示所请求的资源的类型,这部分内容是可选的
D:资源ID,它表示所请求的是一个特定的资源,它通常是一个数字,对应前面我们所介绍的数据库表中的_id字段的内容,它唯一地标志了某一种资源下的一个特定的实例
我们在定义URI的时候一般情况行都是需要定义这个C,但是D就没有定义,简单点说C就是代表了数据库中某一个表(这个名字是随意定义不一定就是和我们的数据库表名字相同,这里只是一个比喻),而D则是表示操作这个表的哪一个字段,相当于我们的SQL语句中 where id=1这个条件
继续往下看:
/*MIME*/ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.com.itchq.contentprovider"; public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.com.itchq.contentprovider";
这个是MIME类型,每一个ContenProvider中都必须定义这个MIME,其实这个MIME类型一般来说就估计上面两种,被分为两部分前面是数据的大类别,后面定义具体的种类,比如上面的vnd.android.cursor.dir和vnd.android.cursor.item都是固定不会变的,后面的字段都是相同的而且随便定义,一般的数据库中都vnd.+包名。对于vnd.android.cursor.dir这个类型表示的访问多个资源的URI,就是你操作的是一整张表,比如添加新数据了,删除某一个表后者是查询表的全部记录等等,而vnd.android.cursor.item是表示访问单个资源的URI,比如更新表中某一个自定的记录,查询某一条指定的信息,就是相当SQL中的where条件语句。
Artice这个接口是我们这个数据库的表的字段,这里看出这个表有三个字段,分别是:_id,title,contentid。ITEM和ITEM_POS这两个变量分别定义URI匹配规则的匹配码。
比如如果我们访问的URI是content://com.itchq.contentprovider/articeitem,则匹配规则返回的匹配码为ITEM。如果访问的是content://com.itchq.contentprovider/articeitem/# # 是一个通配符表示如何数据,则匹配规则返回的匹配码为ITEM_POS,这两个常量我们后面还会有用到的
下面就是ContentProvider的定义了
package com.itchq.contentprovider;import java.util.HashMap;import com.itchq.contentprovider.ItemContentProvier.Artice;import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.text.TextUtils;public class itContentProvider extends ContentProvider{private static final String DB_NAME="itchq.db";private static final String DB_TABLE_ARTICE="artice";private static final int DB_VERSION=1;private DBHelper dbHelper = null; private static final String DB_CREATE_ARTICE = "create table " + DB_TABLE_ARTICE + " (" + Artice.ID + " integer primary key autoincrement, " + Artice.TITLE + " text not null, " + Artice.CONTENT_ID + " integer not null);";private static final UriMatcher uriMatcher;private static final HashMap<String, String> articleProjectionMap;static{uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);uriMatcher.addURI(ItemContentProvier.AUTHORITY, ItemContentProvier.ARTICE_ITEM, ItemContentProvier.ITEM);uriMatcher.addURI(ItemContentProvier.AUTHORITY, ItemContentProvier.ARTICE_ITEM+"/#", ItemContentProvier.ITEM_POS);articleProjectionMap = new HashMap<String, String>(); articleProjectionMap.put(Artice.ID, Artice.ID); articleProjectionMap.put(Artice.TITLE, Artice.TITLE); articleProjectionMap.put(Artice.CONTENT_ID,Artice.CONTENT_ID); }@Overridepublic boolean onCreate() {// TODO Auto-generated method stubdbHelper = new DBHelper(getContext(), DB_NAME, null, DB_VERSION); return false;}@Overridepublic Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {// TODO Auto-generated method stubSQLiteDatabase db = dbHelper.getReadableDatabase(); SQLiteQueryBuilder sqlBuilder = new SQLiteQueryBuilder(); switch(uriMatcher.match(uri)){case ItemContentProvier.ITEM:{sqlBuilder.setTables(DB_TABLE_ARTICE);sqlBuilder.setProjectionMap(articleProjectionMap); break;}case ItemContentProvier.ITEM_POS:{String id = uri.getPathSegments().get(1); sqlBuilder.setTables(DB_TABLE_ARTICE); sqlBuilder.setProjectionMap(articleProjectionMap); sqlBuilder.appendWhere(Artice.ID + "=" + id); break;}default: throw new IllegalArgumentException("Error Uri: " + uri); }Cursor cursor = sqlBuilder.query(db, projection, selection, selectionArgs, null, null, TextUtils.isEmpty(sortOrder) ? ItemContentProvier.DEFAULT_SORT_ORDER : sortOrder);cursor.setNotificationUri(getContext().getContentResolver(), uri); return cursor;}@Overridepublic String getType(Uri uri) {// TODO Auto-generated method stubswitch (uriMatcher.match(uri)) { case ItemContentProvier.ITEM:{return ItemContentProvier.CONTENT_TYPE;}case ItemContentProvier.ITEM_POS:{return ItemContentProvier.CONTENT_ITEM_TYPE;}default:throw new IllegalArgumentException("Error Uri: " + uri); }}@Overridepublic Uri insert(Uri uri, ContentValues values) {// TODO Auto-generated method stubSQLiteDatabase db = dbHelper.getWritableDatabase();long id = db.insert(DB_TABLE_ARTICE, Artice.ID, values); if (id < 0) {throw new SQLiteException("Unable to insert " + values + " for "+ uri);}Uri newUri = ContentUris.withAppendedId(uri, id);getContext().getContentResolver().notifyChange(newUri,null);return newUri;}@Overridepublic int delete(Uri uri, String selection, String[] selectionArgs) {// TODO Auto-generated method stubSQLiteDatabase db = dbHelper.getWritableDatabase(); int count = 0;switch(uriMatcher.match(uri)){case ItemContentProvier.ITEM:{count = db.delete(DB_TABLE_ARTICE, selection, selectionArgs);break;}case ItemContentProvier.ITEM_POS:{String id = uri.getPathSegments().get(1);count = db.delete(DB_TABLE_ARTICE,Artice.ID + "=" + id + (!TextUtils.isEmpty(selection) ? " and (" + selection + ')' : ""), selectionArgs);break;}default:throw new IllegalArgumentException("Error Uri: " + uri);}getContext().getContentResolver().notifyChange(uri, null);return count;}@Overridepublic int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) {// TODO Auto-generated method stubSQLiteDatabase db = dbHelper.getWritableDatabase(); int count = 0;switch(uriMatcher.match(uri)){case ItemContentProvier.ITEM:{count = db.update(DB_TABLE_ARTICE, values, selection, selectionArgs);break;}case ItemContentProvier.ITEM_POS:{String id = uri.getPathSegments().get(1);count = db.update(DB_TABLE_ARTICE, values, Artice.ID + "=" + id + (!TextUtils.isEmpty(selection) ? " and (" + selection + ')' : ""), selectionArgs);break;}default:throw new IllegalArgumentException("Error Uri: " + uri);}getContext().getContentResolver().notifyChange(uri, null);return count;}private static class DBHelper extends SQLiteOpenHelper{public DBHelper(Context context, String name, CursorFactory factory,int version) {super(context, name, factory, version);// TODO Auto-generated constructor stub }@Overridepublic void onCreate(SQLiteDatabase db) {// TODO Auto-generated method stub db.execSQL(DB_CREATE_ARTICE);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {// TODO Auto-generated method stub }}}
继承ContentProvider类需要实现默认下面这些方法
public boolean onCreate() 在创建ContentProvider时调用
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) 用于查询指定Uri的ContentProvider,返回一个Cursor
public int delete(Uri uri, String selection, String[] selectionArgs) 用于从指定Uri的ContentProvider中删除数据
public int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) 用于更新指定Uri的ContentProvider中的数据
public Uri insert(Uri uri, ContentValues values) 用于添加新数据到指定Uri的ContentProvider中
public String getType(Uri uri) 用于返回指定的Uri中的数据的MIME类型
这个就是前面说的,如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头。即
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.com.itchq.contentprovider";
如果要操作的数据属于非集合类型数据(简单点说就是指定where条件的),那么MIME类型字符串应该以vnd.android.cursor.item/开头。即
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.com.itchq.contentprovider";
UriMatcher类用于匹配Uri 这个就是要设置Uri的匹配规则,我们可以根据不同的Uri来操作不同的数据,比如前面我们定义的那个URI
/*Content URI*/public static final Uri CONTENT_ARTICE=Uri.parse("content://"+AUTHORITY+"/"+ARTICE_ITEM);
分解这个URI字符串是”content://com.itchq.contentprovider/articeitem“,那么我们操作可以是content://com.itchq.contentprovider/articeitem这个字符串也也可能是content://com.itchq.contentprovider/articeitem/#这个字符串,前面表示操作整个数据库表,后面指定是操作数据库某一个字段,那我们是如何知道这个匹配码呢?这个就需要UriMatcher类来设定
public void addURI (String authority, String path, int code) ;
authority就是上面定义的public static final String AUTHORITY="com.itchq.contentprovider";这个变量,path就是就是指具体那个路径,一般就是URI规则中表示C的部分,比如上面URI规则中 public static final Uri CONTENT_ARTICE=Uri.parse("content://"+AUTHORITY+"/"+ARTICE_ITEM);那么这个path指的就是ARTICE_ITEM这个字符串变量,即public static final String ARTICE_ITEM="articeitem"; code就是后面那个匹配码,比如我们可以看一下update更新语句里面有这么一个switch函数:
switch(uriMatcher.match(uri)){case ItemContentProvier.ITEM:{}case ItemContentProvier.ITEM_POS:{}default:throw new IllegalArgumentException("Error Uri: " + uri);}
上面的ItemContentProvier.ITEM 和 ItemContentProvier.ITEM_POS 就是已经定义好的匹配码,即code参数,我们通过这个ItemContentProvier.ITEM可以知道操作整个数据库表,ItemContentProvier.ITEM_POS操作的是数据库表中某一列,这个匹配一般都是一开始初始化的时候就需要全部给注册上的,
static{uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);uriMatcher.addURI(ItemContentProvier.AUTHORITY, ItemContentProvier.ARTICE_ITEM, ItemContentProvier.ITEM);uriMatcher.addURI(ItemContentProvier.AUTHORITY, ItemContentProvier.ARTICE_ITEM+"/#", ItemContentProvier.ITEM_POS);
这个就是注册上的,我们先看这个path参数有ItemContentProvier.ARTICE_ITEM和ItemContentProvier.ARTICE_ITEM/#,这两个就是分别表示操作的数据库表和数据库对应某一列,最后一个参数就是我们上面也说到的匹配码,构造函数的参数为UriMatcher.NO_MATCH,它表示当uriMatcher不能匹配指定的URI时,就返回代码UriMatcher.NO_MATCH常量值为-1
public int match (Uri uri)
与传入的Uri匹配,它会首先与找我们之前通过addURI方法添加进来的Uri匹配,这个URI匹配是这样的,前面部分固定是A部分:content://, B部分是authority,也就是我们addURI里面添加的第一个参数,C部分表示是路径,也就是我们在addRUI添加的第二个参数path,就是说最后匹配的URI就是这样的 content://authority/path。
authority和path都是我们在addURI参数就第一个和第二个的参数。如果匹配成功就返回之前我们设置的code值,否则返回一个UriMatcher.NO_MATCH常量值为-1
articleProjectionMap = new HashMap<String, String>(); articleProjectionMap.put(Artice.ID, Artice.ID); articleProjectionMap.put(Artice.TITLE, Artice.TITLE); articleProjectionMap.put(Artice.CONTENT_ID,Artice.CONTENT_ID);
在上面的put函数中,第一个参数表示列的别名,第二个参数表示列的真实名称。在这个例子中,我们把列的别名和和真实名称都设置成一样的。
在query函数中,我们使用SQLiteQueryBuilder来辅助数据库查询操作,使用这 个类的好处是我们可以不把数据库表的字段暴露出来,而是提供别名给第三方应用程序使用,这样就可以把数据库表内部设计隐藏起来,方便后续扩展和维护。列别 名到真实列名的映射是由上面这个HashMap成员变量来实现的,当然这里没有修改,也可以不用定义。
@Overridepublic String getType(Uri uri) {// TODO Auto-generated method stubswitch (uriMatcher.match(uri)) { case ItemContentProvier.ITEM:{return ItemContentProvier.CONTENT_TYPE;}case ItemContentProvier.ITEM_POS:{return ItemContentProvier.CONTENT_ITEM_TYPE;}default:throw new IllegalArgumentException("Error Uri: " + uri); }}
这个是返回当前Uri所数据的MIME类型,如果该Uri对应的数据可能包括多条记录,那么MIME类型字符串就是以 vnd.android.cursor.dir/开头,我们通过前面的addURI指定 ItemContentProvier.ITEM是返回全面的类型,如果Uri对应的数据只包含一条记录,那么MIME类型字符串就是以 vnd.android.cursor.item/开头,前面的addURI得知ItemContentProvier.ITEM_POS这个返回的是某一列信息。
还有一点是我们在itContentProvider类的内部中定义了一个DBHelper类,它继承于SQLiteOpenHelper类,它用是用辅助我 们操作数据库的。使用这个DBHelper类来辅助操作数据库的好处是只有当我们第一次对数据库时行操作时,系统才会执行打开数据库文件的操作,
剩下的更新查询删除等操作就不多说了,最后不要忘记在AndroidManifest.xml中进行定义
<providerandroid:name="com.itchq.contentprovider.itContentProvider"android:authorities="com.itchq.contentprovider"android:multiprocess="false"/>
在配置Content Provider的时候,最重要的就是要指定它的authorities属性了,只有配置了这个属性,第三方应用程序才能通过它来找到这个Content Provider。这要需要注意的,这里配置的authorities属性的值是和我们前面在ItemContentProvier.java文件中定义的AUTHORITY 常量的值是一致的。另外一个属性multiprocess是一个布尔值,它表示这个Content Provider是否可以在每个客户进程中创建一个实例,这样做的目的是为了减少进程间通信的开销。这里我们为了减少不必要的内存开销,把属性 multiprocess的值设置为false,使得系统只能有一个Content Provider实例存在,它运行在自己的进程中。下面看看在Activity中投入和操作这些数据库的
private void add(){ContentValues values = new ContentValues();values.put(Artice.TITLE, "test");values.put(Artice.CONTENT_ID, 1);Uri uri = getContentResolver().insert(ItemContentProvier.CONTENT_ARTICE, values);}private void query(){String[] projection = new String[] { Artice.ID, Artice.TITLE, Artice.CONTENT_ID};Cursor cursor = getContentResolver().query(ItemContentProvier.CONTENT_ARTICE, projection, null, null, ItemContentProvier.DEFAULT_SORT_ORDER);if (cursor.moveToFirst()) {do{int id=cursor.getInt(cursor.getColumnIndex(Artice.ID));String title=cursor.getString(cursor.getColumnIndex(Artice.TITLE));int content_id=cursor.getInt(cursor.getColumnIndex(Artice.CONTENT_ID));}while(cursor.moveToNext());}}
ItemContentProvier.CONTENT_ARTICE就是我们前面定义的那个URI这个介绍一个ContentUris类,这个类是它用于在Uri后面追加一个ID或者解析出传入的Uri所带上的ID值,就是我们说的URI规则中最后D部分|------A------|-----------------B-------------------|---C---|---D--|常用的两个方法如下:public static Uri withAppendedId (Uri contentUri, long id) 用于为路径加上ID部分Uri resultUri = ContentUris.withAppendedId(ItemContentProvier.CONTENT_ARTICE, 10);那么这个Uri就是content://com.itchq.contentprovider/articeitem/10,那么我们在前面使用UriMatch.addURI匹配码就是uriMatcher.addURI(ItemContentProvier.AUTHORITY, ItemContentProvier.ARTICE_ITEM+"/#", ItemContentProvier.ITEM_POS);这个,那么这个时候返回的匹配码就是 ItemContentProvier.ITEM_POS。 public static long parseId (Uri contentUri)从路径中获取ID部分Uri uri = Uri.parse("content://com.itchq.contentprovider/articeitem/10"long personid = ContentUris.parseId(uri);//获取的结果为:10,前面的操作中有用到这个
转载于:https://www.cnblogs.com/itchq/p/4014388.html
Android ContentProvider介绍相关推荐
- android AsyncTask介绍(转)
android AsyncTask介绍 AsyncTask和Handler对比 1 ) AsyncTask实现的原理,和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接 ...
- Android ContentProvider支持跨进程数据共享与互斥、同步 杂谈
在开发中,假如,A.B进程有部分信息需要同步,这个时候怎么处理呢?设想这么一个场景,有个业务复杂的Activity非常占用内存,并引发OOM,所以,想要把这个Activity放到单独进程,以保证OOM ...
- Android类库介绍
Android类库介绍 GPhone开发包Android SDK含了很多丰富的类库: android.util 涉及系统底层的辅助类库 android.os 提供了系统服务.消息传输.IPC管道 an ...
- 1.android体系结构介绍
一.Android的介绍 android介绍见百度百科:Android的介绍,度娘把Android介绍的这么清楚,如果谷歌是Android的爹,那度娘就是娘了. 二.Android的架构图 andro ...
- 【转】Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析
原文网址:http://blog.csdn.net/xubin341719/article/details/38584469 关键词:蓝牙blueZ A2DP.SINK.sink_connect.s ...
- 作业Android自我介绍
***Android自我介绍***
- Android HIDL 介绍学习之客户端调用
应上一篇文章Android HIDL 介绍学习_Super Jang的博客-CSDN博客_安卓hidl读者的要求,本文更新客户端调用方法. hidl的客户端调用相比服务端的实现要简单很多,本次我们通过 ...
- Android开发介绍
Android开发介绍 这个教程设计是帮助你学习基础的Android开发和快速搭建您的开发环境.这个是在我的Window 7上编写的,当然它也支持Linux(uBuntu).Mac OS其他支持And ...
- android 模拟器的使用(Android模拟器介绍及创建)
做了这么久的android开发,一直想把在开发过程中积累的经验或问题总结一下,可是都没有时间,现在有一点点空闲,所以慢慢把我在android中遇到的问题整理一下写出来,供自己以后参考,也希望和大家一起 ...
最新文章
- mirna富集分析_2020年的3+分ceRNA分析长啥样?
- 在Linux系统下查看ora错误
- 脑裂问题解决方案_从解决方案到问题
- 计算机少年宫辅导教师总结,微机兴趣小组活动总结
- perl 中部分正则表达式中匹配非空字符和正常使用字符
- glob,正则表达式元字符,扩展正则表达式总结
- 大话西游之程序员做项目
- hadoop2.7的目录结构
- 算法导论第三版 第30章习题答案
- 阿里云短信验证码开发
- 迅速崛起 盘点2018年中国AI芯片“四小龙”
- 《鬼武者3》全攻略宝典
- 2021年胡润中国百富榜研究报告
- python爬取歌词生成词云图
- [WDS]Disconnected!
- python批量修改文件的后缀名
- 经典动漫秒变高清,需要怎么做?
- springboot+vue3+微信小程序记账本源码
- 基本磁盘无法转化为动态磁盘,提示:此对象不支持操作
- 虚拟opc服务器软件,OPC Server
热门文章
- http://www.himigame.com/mac-cocoa-application/893.html
- 第二次团队作业-需求分析
- 操作系统:Android(Google公司开发的操作系统)
- RedisDesktopManager连接不上redis的解决方法
- 谷歌浏览器查询缓存视频图片
- 数字万用表的四位半,三位半都是什么意思?
- SQL:RAND()返回随机数
- Linux-鸟菜-6-文件与目录管理
- 【Linux 内核】SMP 对称多处理器结构 ( SMP 对称多处理器结构概念 | SMP 对称多处理器结构的优势与缺陷 | Linux 内核兼容多处理器要求 )
- 【Groovy】闭包 Closure ( 闭包调用 | 闭包默认参数 it | 代码示例 )