ContentProvider数据访问详解
ContentProvider数据访问详解
Android官方指出的数据存储方式总共有五种:Shared Preferences、网络存储、文件存储、外储存储、SQLite,这些存储方式一般都只是在一个单独的应用程序中实现数据的共享,而对于需要操作其他应用程序中的数据时(如媒体库、通讯录等),可能就需要借助ContentProvider了。
1、ContentProvider
ContentProvider为存储和获取数据提供了统一的接口,使用表的形式来对数据进行封装,使得开发者在后续的开发过程中不用关心数据存储的细节。使用ContentProvider可以在不同的应用程序之间共享数据,Android为常见的数据类型提供了默认的ContentProvider(包括音频、视频、图片和通讯录等)。
总的来说,利用ContentProvider来实现共享数据的好处是统一了数据的访问方式。
2、URI(Uniform Resource Identifier)
URI为系统中的每一个资源赋予一个名字,比方说通话记录。每一个ContentProvider都拥有一个公共的URI,用于表示ContentProvider所提供的数据。 Android所提供的ContentProvider都位于android.provider包中, 可以将URI分为A、B、C、D 4个部分来理解。如对于content://com.wang.provider.myprovider/tablename/id:
a、标准前缀——content://,用来说明一个Content Provider控制这些数据;
b、URI的标识——com.wang.provider.myprovider,用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的、小写的类名。这个标识在元素的authorities属性中说明,一般是定义该ContentProvider的包.类的名称;
c、路径——tablename,通俗的讲就是你要操作的数据库中表的名字,或者你也可以自己定义,记得在使用的时候保持一致就可以了;
d、记录ID——id,如果URI中包含表示需要获取的记录的ID,则返回该id对应的数据,如果没有ID,就表示返回全部;
对于第三部分路径(path)做进一步的解释,用来表示要操作的数据,构建时应根据实际项目需求而定。如:
a、操作tablename表中id为11的记录,构建路径:/tablename/11;
b、操作tablename表中id为11的记录的name字段:tablename/11/name;
c、操作tablename表中的所有记录:/tablename;
d、操作来自文件、xml或网络等其他存储方式的数据,如要操作xml文件中tablename节点下name字段:/ tablename/name;
e、若需要将一个字符串转换成Uri,可以使用Uri类中的parse()方法,如:
Uri uri = Uri.parse("content://com.wang.provider.myprovider/tablename");
3、UriMatcher
Uri代表要操作的数据,在开发过程中对数据进行获取时需要解析Uri,Android提供了两个用于操作Uri的工具类,分别为UriMatcher和ContentUris 。掌握它们的基本概念和使用方法,对一个Android开发者来说是一项必要的技能。
UriMatcher类用于匹配Uri,它的使用步骤如下:
a、将需要匹配的Uri路径进行注册,代码如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配“content://com.wang.provider.myprovider/tablename”路径,返回匹配码为1
sMatcher.addURI("content://com.wang.provider.myprovider", " tablename ", 1);
//如果match()方法匹配content://com.wang.provider.myprovider/tablename/11路径,返回匹配码为2
sMatcher.addURI("com.wang.provider.myprovider", "tablename/#", 2);
注意,添加第二个URI时,路径后面的id采用了通配符形式“#”,表示只要前面三个部分都匹配上了就OK。
b、注册完需要匹配的Uri后,可以使用sMatcher.match(Uri)方法对输入的Uri进行匹配,如果匹配就返回对应的匹配码,匹配码为调用addURI()方法时传入的第三个参数。
switch (sMatcher.match(Uri.parse("content://com.zhang.provider.yourprovider/tablename/100"))) {
case 1:
//match 1, todo something
break;
case 2
//match 2, todo something
break;
default:
//match nothing, todo something
break;
}
4、ContentUris
ContentUris类用于操作Uri路径后面的ID部分,它有两个比较实用的方法:withAppendedId(Uri uri, long id)和parseId(Uri uri)。
withAppendedId(Uri uri, long id)用于为路径加上ID部分:
Uri uri = Uri.parse("content://com.wang.provider.myprovider/tablename");
Uri resultUri = ContentUris.withAppendedId(uri, 10);
parseId(Uri uri)则从路径中获取ID部分:
Uri uri = Uri.parse("content://com.zhang.provider.myprovider/tablename/10")
long personid = ContentUris.parseId(uri);
5、ContentProvider数据共享
ContentProvider类主要方法的介绍:
public boolean onCreate(),在ContentProvider创建后就会被调用,而ContentProvider是在其它应用第一次访问它时被创建;
public Uri insert(Uri uri, ContentValues values),供外部应用向ContentProvider添加数据;
public int delete(Uri uri, String selection, String[] selectionArgs),供外部应用从ContentProvider删除数据;
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs),供外部应用更新ContentProvider中的数据;
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder),供外部应用从ContentProvider中获取数据;
public String getType(Uri uri),返回当前Uri所代表数据的MIME类型;
如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,如要得到所有tablename记录的Uri为content://com.wang.provider.myprovider/tablename,那么返回的MIME类型字符串应该为:vnd.android.cursor.dir/table。
如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,如得到id为10的tablename记录,Uri为content://com.wang.provider.myprovider/tablename/10,那么返回的MIME类型字符串为:vnd.android.cursor.item/tablename 。
6、ContentResolver操作数据
当外部应用需要对ContentProvider中的数据进行添加、删除、修改及查询操作时,可以使用ContentResolver 类来完成。而要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()方法。
ContentResolver 类提供了与ContentProvider类相同签名的四个方法:
public Uri insert(Uri uri, ContentValues values),往ContentProvider添加数据;
public int delete(Uri uri, String selection, String[] selectionArgs),从ContentProvider删除数据;
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs),更新ContentProvider中的数据;
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder),从ContentProvider中获取数据;
这些方法的第一个参数为Uri,代表要操作的ContentProvider和对其中的什么数据进行操作,其实和ContentProvider里面的方法是一样的。他们所对应的数据,最终会被传到我们在之前程序里面定义的那个ContentProvider类的方法,假设给定的是:Uri.parse("content://com.wang.provider.myprovider/tablename/10"),那么将会对主机名为com.wang.provider.myprovider的ContentProvider进行操作,操作的数据为tablename表中id为10的记录。
使用ContentResolver对ContentProvider中的数据进行操作的代码如下:
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://com.wang.provider.myprovider/tablename");
//添加一条记录
ContentValues values = new ContentValues();
values.put("name", "wang1");
values.put("age", 28);
resolver.insert(uri, values);
//获取tablename表中所有记录
Cursor cursor = resolver.query(uri, null, null, null, "tablename data");
while(cursor.moveToNext()){
Log.i("ContentTest", "tablename_id="+ cursor.getInt(0)+ ", name="+ cursor.getString(1));
}
//把id为1的记录的name字段值更改新为zhang1
ContentValues updateValues = new ContentValues();
updateValues.put("name", "zhang1");
Uri updateIdUri = ContentUris.withAppendedId(uri, 2);
resolver.update(updateIdUri, updateValues, null, null);
//删除id为2的记录,即字段age
Uri deleteIdUri = ContentUris.withAppendedId(uri, 2);
resolver.delete(deleteIdUri, null, null);
7、监听数据变化
如果ContentProvider的访问者需要知道数据发生的变化,可以在ContentProvider发生数据变化时调用getContentResolver().notifyChange(uri, null)来通知注册在此URI上的访问者。只给出类中监听部分的代码:
public class MyProvider extends ContentProvider {
public Uri insert(Uri uri, ContentValues values) {
db.insert("tablename", "tablenameid", values);
getContext().getContentResolver().notifyChange(uri, null);
}
}
而访问者必须使用ContentObserver对数据(数据采用uri描述)进行监听,当监听到数据变化通知时,系统就会调用ContentObserver的onChange()方法:
getContentResolver().registerContentObserver(Uri.parse("content://com.ljq.providers.personprovider/person"),
true, new PersonObserver(new Handler()));
public class PersonObserver extends ContentObserver{
public PersonObserver(Handler handler) {
super(handler);
}
public void onChange(boolean selfChange) {
//to do something
}
}
8、完整项目代码
a、对于实际测试中的Uri——com.wang.provider.myprovider,需要在AndroidManifest.xml文件中进行说明:
<provider android:name="MyProvider" android:authorities="com.wang.provider.myprovider" />
b、定义Profile类,其主要负责定义Uri各组成部分名称对应的String常量:
package com.wang.testcontentprovider;
import android.net.Uri;
public class Profile {
/**
* 表格名称
*/
public static final String TABLE_NAME = "tablename";
/**
* 列表一,_ID,自动增加
*/
public static final String COLUMN_ID = "_id";
/**
* 列表二,名称
*/
public static final String COLUMN_NAME = "name";
public static final String AUTOHORITY = "com.wang.provider.myprovider";
public static final int ITEM = 1;
public static final int ITEM_ID = 2;
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.wang.tablename";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.wang.tablename";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTOHORITY + "/tablename");
}
c、实现存放数据的DBHelper类,继承自SQLiteOpenHelper类,完成数据库的一些常规操作:
package com.wang.testcontentprovider; import android.content.Context;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; public class DBHelper extends SQLiteOpenHelper { /**
* 数据库名称
*/
private static final String DATABASE_NAME = "test.db"; /**
* 数据库版本
*/
private static final int DATABASE_VERSION = 1; public DBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
} @Override
public void onCreate(SQLiteDatabase db) throws SQLException {
//创建表格
db.execSQL("CREATE TABLE IF NOT EXISTS "+ Profile.TABLE_NAME + "("+ Profile.COLUMN_ID +" INTEGER PRIMARY KEY AUTOINCREMENT," + Profile.COLUMN_NAME +" VARCHAR NOT NULL);");
} @Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) throws SQLException {
//删除并创建表格
db.execSQL("DROP TABLE IF EXISTS "+ Profile.TABLE_NAME+";");
onCreate(db);
}
}
d、MyProvider类的实现,主要负责向ContentProvider中添加数据
package com.wang.testcontentprovider; import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri; public class MyProvider extends ContentProvider { DBHelper mDbHelper = null;
SQLiteDatabase db = null; private static final UriMatcher mMatcher;
static{
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mMatcher.addURI(Profile.AUTOHORITY,Profile.TABLE_NAME, Profile.ITEM);
mMatcher.addURI(Profile.AUTOHORITY, Profile.TABLE_NAME+"/#", Profile.ITEM_ID);
} @Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
return 0;
} @Override
public String getType(Uri uri) {
switch (mMatcher.match(uri)) {
case Profile.ITEM:
return Profile.CONTENT_TYPE;
case Profile.ITEM_ID:
return Profile.CONTENT_ITEM_TYPE;
default:
throw new IllegalArgumentException("Unknown URI"+uri);
}
} @Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
long rowId;
if(mMatcher.match(uri)!=Profile.ITEM){
throw new IllegalArgumentException("Unknown URI"+uri);
}
rowId = db.insert(Profile.TABLE_NAME,null,values);
if(rowId>0){
Uri noteUri=ContentUris.withAppendedId(Profile.CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(noteUri, null);
return noteUri;
} throw new SQLException("Failed to insert row into " + uri);
} @Override
public boolean onCreate() {
// TODO Auto-generated method stub
mDbHelper = new DBHelper(getContext()); db = mDbHelper.getReadableDatabase(); return true;
} @Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO Auto-generated method stub
Cursor c = null;
switch (mMatcher.match(uri)) {
case Profile.ITEM:
c = db.query(Profile.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
break;
case Profile.ITEM_ID:
c = db.query(Profile.TABLE_NAME, projection,Profile.COLUMN_ID + "="+uri.getLastPathSegment(), selectionArgs, null, null, sortOrder);
break;
default:
throw new IllegalArgumentException("Unknown URI"+uri);
} c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
} @Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO Auto-generated method stub
return 0;
} }
e、作为主程序的MainActivity类,对ContentProvider中的数据进行获取,并在界面上进行简单的显示:
package com.wang.testcontentprovider; import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem; import android.app.ListActivity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.widget.SimpleCursorAdapter; public class MainActivity extends ListActivity {
private SimpleCursorAdapter adapter= null;
private Cursor mCursor = null;
private ContentResolver mContentResolver = null; @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initData();
initAdapter();
} public void initData(){
mContentResolver = getContentResolver();
for (int i = 0; i < 100; i++) {
ContentValues values = new ContentValues();
values.put(Profile.COLUMN_NAME, "Wang "+i);
mContentResolver.insert(Profile.CONTENT_URI, values);
}
} public void initAdapter(){
mCursor = mContentResolver.query(Profile.CONTENT_URI, new String[]{Profile.COLUMN_ID,Profile.COLUMN_NAME}, null, null, null); startManagingCursor(mCursor); //设置adapter
adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, mCursor, new String[]{Profile.COLUMN_ID,Profile.COLUMN_NAME}, new int[]{android.R.id.text1,android.R.id.text2});
setListAdapter(adapter);
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
} }
9、结果分析
贴上一张不太美观的结果图:

ContentProvider数据访问详解的更多相关文章
- 【HANA系列】SAP HANA XS使用JavaScript数据交互详解
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列]SAP HANA XS使用Jav ...
- 【HANA系列】【第一篇】SAP HANA XS使用JavaScript数据交互详解
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列][第一篇]SAP HANA XS ...
- JVM 运行时数据区详解
一.运行时数据区 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同数据区域. 1.有一些是随虚拟机的启动而创建,随虚拟机的退出而销毁,所有的线程共享这些数据区. 2.第二种则 ...
- 学习《深度学习与计算机视觉算法原理框架应用》《大数据架构详解从数据获取到深度学习》PDF代码
<深度学习与计算机视觉 算法原理.框架应用>全书共13章,分为2篇,第1篇基础知识,第2篇实例精讲.用通俗易懂的文字表达公式背后的原理,实例部分提供了一些工具,很实用. <大数据架构 ...
- 3dTiles 数据规范详解[1] 介绍
版权:转载请带原地址.https://www.cnblogs.com/onsummer/p/12799366.html @秋意正寒 Web中的三维 html5和webgl技术使得浏览器三维变成了可能. ...
- HTTP数据包详解
无论Web技术在未来如何发展,理解Web程序之间通信的基本协议相当重要, 因为它让我们理解了Web应用程序的内部工作. 本文将对HTTP协议进行详细的实例讲解,内容较多,希望大家耐心看. 阅读目录 ...
- [jvm]运行时数据区域详解
了解虚拟机是怎么使用内存的,有助于我们解决和排查内存泄漏和溢出方面的问题.详解java虚拟机内存的各个区域,分析这些区域的作用服务对象以及可能发生的问题. 一.运行时数据区域 java虚拟机在执行ja ...
- hbase实践之数据读取详解
hbase基本存储组织结构与数据读取组织结构对比 Segment是Hbase2.0的概念,MemStore由一个可写的Segment,以及一个或多个不可写的Segments构成.故hbase 1.*版 ...
- Android基础内容提供者ContentProvider的使用详解(转)
1.什么是ContentProvider 首先,ContentProvider(内容提供者)是android中的四大组件之一,但是在一般的开发中,可能使用的比较少. ContentProvider为不 ...
随机推荐
- Hadoop HDFS Basic JAVA API
org.apache.hadoop.fs.FileSystem 是HDFS的文件系统抽象,在分布式系统中管理HDFS文件和目录.文件内容存储在由多个相同大小的块(如64M)构成的datanode节 ...
- python基础(六)循环
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 循环用于重复执行一些程序块.从上一讲的选择结构,我们已经看到了如何用缩进来表示程序 ...
- x01.BitmapHelper:图像处理
“所有致我于死地的,也激发我胆魄”,姚贝娜的<心火>,是我近年来听过最好的歌,特此推荐一下. 图像处理,大概分三步:1.LockBits():2.进行处理:3.UnlockBits():这 ...
- python enumerate函数用法
enumerate函数用于遍历序列中的元素以及它们的下标 i = 0 seq = ['one', 'two', 'three'] for element in seq: print i, seq[i] ...
- Ubuntu14.04桌面版基本配置
最近公司需要用Ubuntu测试一些功能,安装好后绚丽的桌面环境很是吸引人,但切换到字符界面后,发现不少地方与RHEL不一样,连ssh.vim都没有,字符界面玩得不开心. 搜了不少文章,才将基本的功能配 ...
- C#编程普通型计算器 经验与感悟
先贴图: 这是用C# 语言编写的普通型计算器,功能基本模仿Windows8自带计算器程序(版本6.3,内部版本9600).支持加.减.乘.除.退格.清除.平方根.倒数.相反数.连续四则.连续等号.自动 ...
- [书目20160526]Brain Rules 让大脑自由:释放天赋的12条定律
推荐序1 12条定律,让大脑更聪明推荐序2 走过迷雾地带前 言 人人都有一个不可思议的大脑 定律1:越运动,大脑越聪明 信不信,“驴友”比“沙发土豆”更聪明! 老板,请把办公室的咖啡机换成跑步机! ...
- 思科ACI是一种什么样的技术?
术语: SDN:Software Defined Network,软件定义网络 ACI:Application Centric Infrastracture,以应用为中心的基础网络架构 Fabric: ...
- Network Experiment Environment Construction
In order to provide an environment for network experiments in the future, I use VirutalBox to create ...
- HDU 2117 取(2堆)石子游戏【wzf博弈】
题意:威佐夫博弈原型,除了输出先手能不能胜,还要输出先手的第一手选择. 思路:预处理出1000000以内的所有奇异局势.对于每个自然数,其必然是某一个奇异局势的a或者b.故对于一个非奇异局势,必定有一 ...