解读ContentResolver和ContentProvider
转自:http://cthhqu.blog.51cto.com/7598297/1281217
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
package cth.android.contentprovide; import android.annotation.SuppressLint; import android.app.Activity; import android.content.ContentResolver; import android.database.Cursor; import android.os.Bundle; import android.provider.ContactsContract; import android.util.Log; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.SimpleCursorAdapter; /**
* @author CTH
*
*该类是演示利用ContentProvider,获取手机的联系人信息。
*使用ContentProvide的步骤:
*1、从当前Activity获取系统的ContentResolver;
*2、使用ContentProvider的insert、delete、update、query方法对ContentProvider的内容进行增删改查;
*3、如果是使用query是的到一个Cursor的结果集,通过该结果集可以获得我们查询的结果。
*
*/ public class MainActivity extends Activity {
private ListView contactsList;
@SuppressLint ( "InlinedApi" )
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
//setContentView(R.layout.activity_main); //不使用inflate XML文件方法,而是使用动态生成控件。
contactsList = new ListView(MainActivity. this );
ContentResolver cr = getContentResolver(); //获取ContentResolver
Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI, null ,
null , null , null ); //查询系统的联系人信息
Log.i( "cth" , cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME) + "" );
@SuppressWarnings ( "deprecation" )
ListAdapter la = new SimpleCursorAdapter(MainActivity. this ,
android.R.layout.simple_list_item_1, cursor,
new String[] {ContactsContract.Contacts.DISPLAY_NAME_PRIMARY },
new int [] { android.R.id.text1 }); //建立列表适配器,把cursor关联进来
contactsList.setAdapter(la); //把ListVIew与适配器绑定
setContentView(contactsList); //动态生成ListView
} } |
联系人的URI:
ContactsContract.Contacts.CONTENT_URI 管理联系人的Uri
ContactsContract.CommonDataKinds.Phone.CONTENT_URI 管理联系人的电话的Uri
ContactsContract.CommonDataKinds.Email.CONTENT_URI 管理联系人的Email的Uri
(注:Contacts有两个表,分别是rawContact和Data,rawContact记录了用户的id和name,
其中id栏名称 为:ContactsContract.Contacts._ID,name名称栏为ContactContract.Contracts.DISPLAY_NAME,
电话信息表的外键id为 ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
电话号码栏名称为:ContactsContract.CommonDataKinds.Phone.NUMBER。
data表中Email地址栏名称为:ContactsContract.CommonDataKinds.Email.DATA
其外键栏为:ContactsContract.CommonDataKinds.Email.CONTACT_ID)
多媒体的ContentProvider的Uri如下:
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI 存储在sd卡上的音频文件
MediaStore.Audio.Media.INTERNAL_CONTENT_URI 存储在手机内部存储器上的音频文件
MediaStore.Audio.Images.EXTERNAL_CONTENT_URI SD卡上的图片文件内容
MediaStore.Audio.Images.INTERNAL_CONTENT_URI 手机内部存储器上的图片
MediaStore.Audio.Video.EXTERNAL_CONTENT_URI SD卡上的视频
MediaStore.Audio.Video.INTERNAL_CONTENT_URI 手机内部存储器上的视频
(注:图片的显示名栏:Media.DISPLAY_NAME,图片的详细描述栏为:Media.DESCRIPTION 图片的保存位置:Media.DATA
短信URI: Content://sms
发送箱中的短信URI: Content://sms/outbox
content://authority/path/id
content:
- The scheme portion of the URI. This is always set to
ContentResolver.SCHEME_CONTENT
(valuecontent://
). - authority
- A string that identifies the entire content provider. All the content URIs for the provider start with this string.
- To guarantee a unique authority, providers should consider using an authority that is the same as the provider class' package identifier.
- path
- Zero or more segments, separated by a forward slash (
/
), that identify some subset of the provider's data. - Most providers use the path part to identify individual tables. Individual segments in the path are often called "directories"
- although they do not refer to file directories. The right-most segment in a path is often called a "twig"
- id
- A unique numeric identifier for a single row in the subset of data identified by the preceding path part.
- Most providers recognize content URIs that contain an id part and give them special handling.
- A table that contains a column named
_ID
often expects the id part to be a particular value for that column.
由上述我们可以得到该URI分为四部分:
第一部分:“content://”是系统规定的;
第二部分:authority是一个标志整个内容提供者的字符串,也就是该内容提供者的标识符,相当于我们每个人都有的姓名;
第三部分:是内容提供者提供数据的某个子集,因为内容提供者有时会提供多个数据,我们具体是要访问那个子集,需要把具体路径标出来;
第四部分:是一个标志数据子集中具体某一行数据的数字,一般我们数据库都会有ID这一项,通过该字段可直接定位到表中的某一行。如果不知道或用不到该项可不填。
Android里面提供了两个类对URI进行操作,一个是UriMatcher,该类专用于在ContentProvider中建立匹配器,从安卓的API文档我们得知该匹配器用于在ContentProvider类的最开始建立匹配器并添加匹配规则,在后面的方法覆盖过程,需要用到该类的match方法进行匹配,返回匹配到的标志位。
void | addURI(String authority, String path, int code) // 三个参数,第一个是授权信息,第二个是路径,第三个就是当匹配该规则是返回的标志位
Add a URI to match, and the code to return when this URI is matched.
|
另外一个是ContentUri类,该类仅有的三个方法都是静态类,它是一个提供对URI和ID进行操作的工具类。常用的主要是parseId:用于取得URI中的Id,其实内部实现方法就是取得Uri PATH部分后面的值;withAppendedId用于把ID拼接到一个Uri的后面。
Public Methods | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
static Uri.Builder | appendId(Uri.Builder builder, long id)
Appends the given ID to the end of the path.
|
||||||||||
static long | parseId(Uri contentUri)
Converts the last path segment to a long.
|
||||||||||
static Uri | withAppendedId(Uri contentUri, long id)
Appends the given ID to the end of the path.
|
2.3.2 ContentProvider的使用方法:
了解了什么是URI就可以自定义一个ContentProvider,对外提供访问我们数据的接口了。下面以ContentProvider最常封装的数据类型——数据库为例子进行说明。
整个自定义ContentProvider的过程分为两大步,第一步当然是建立数据源,第二部则是建立访问数据源的内容提供者,这里以SQL数据库为例:
第一步:首先我们需要先建一个SQLiteOpenHelper的子类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; public class StuDbHelper extends SQLiteOpenHelper {
private static String DbName = "student.db" ;
public StuDbHelper(Context context, int version) {
super (context, DbName, null , version);
}
@Override
public void onCreate(SQLiteDatabase db) {
String sql = "create table student (id integer primary key,name varchar(20),age integer)" ; //在数据库中创建一张表
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
} } |
第二步:有了数据源,我们接下来就得建立访问数据源的ContentProvider,建立ContentProvider的步骤通常分为:建立匹配器并添加匹配规则,重写各种操作数据的方法。添加匹配规则必需在所有方法和构造函数的执行前执行,因为有时候我们需要在匹配成功后才建立数据库的SQLiteOpenHelper。所以这里必须使用静态代码块添加匹配规则。而重写访问数据的方法主要有增删改查四种,重写的步骤基本遵循: 对Uri进行匹配 --> 获取读或写数据库对象 --> 根据匹配结果利用switch语句判断该执行哪种操作 --> 返回结果。一下是ContentProvider的源码示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
package cth.android.contentprovider; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; public class StuDbCP extends ContentProvider {
private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); //创建该内容提供者的匹配器
private static final String AUTHORITY = "cth.android.contentprovider.StuDbCP" ; //定义授权信息,用于获取该内容提供者的标识
private static final String PATH = "student" ; //路径,表示访问内容提供者的具体路径,一般用表明表示访问该数据库的具体哪张表
private static final int STU = 1 ; //设定标志位,STU表示单条信息
private static final int STUS = 2 ; //STUS表示多条信息
static {
URI_MATCHER.addURI(AUTHORITY, PATH + "/#" , STU); //给匹配器加入匹配规则
URI_MATCHER.addURI(AUTHORITY, PATH, STUS);
}
private StuDbHelper stuDbHepler = null ;
private SQLiteDatabase stuDb = null ;
@Override
public boolean onCreate() {
boolean flag = false ;
stuDbHepler = new StuDbHelper(getContext(), 1 ); //创建SQLiteOpenHelper对象,版本为1
if (stuDb != null ) flag = true ;
return flag;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
int flag = URI_MATCHER.match(uri); //匹配传入的Uri
Cursor selectResult = null ;
SQLiteDatabase db = stuDbHepler.getReadableDatabase();
String whereClause = null ;
switch (flag) {
case STU:
whereClause = "id = " + ContentUris.parseId(uri); //如果匹配第一种方式表示后面跟了id,所以要先提取id
if (selection != null && selection.equals( "" )) {
whereClause += " and " + selection; //把id加到where条件子句(下面几种方法此步骤作用同理)
}
selectResult = db.query( "student" , projection, whereClause, selectionArgs, null , null , null );
break ;
case STUS:
selectResult = db.query( "student" , projection,selection,selectionArgs, null , null , null );
}
return selectResult;
}
/*返回uri的路径扩展部分的信息,一般单个条目返回字段为vnd.android.cursor.item/对应的PATH 而多个条目的以vnd.android.cursor.dir/对应的PATH。*/
@Override
public String getType(Uri uri) {
int flag = URI_MATCHER.match(uri);
switch (flag) {
case STU:
return "vnd.android.cursor.item/student" ;
case STUS:
return "vnd.android.cursor.dir/students" ;
}
return null ;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
int flag = URI_MATCHER.match(uri);
Uri resultUri = null ;
switch (flag) {
case STUS :
stuDb = stuDbHepler.getWritableDatabase(); //获取写数据库
long id = stuDb.insert(PATH, null , values); //插入数据
resultUri = ContentUris.withAppendedId(uri, id); //建立插入的数据的URI
break ;
}
return resultUri;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int flag = URI_MATCHER.match(uri);
String whereClause = null ;
int rowCount = - 1 ;
SQLiteDatabase db = stuDbHepler.getWritableDatabase();
switch (flag) {
case STU:
whereClause = "id = " + ContentUris.parseId(uri);
if (selection != null && selection.equals( "" )) {
whereClause += " and " + selection ;
}
rowCount = db.delete( "student" , whereClause, selectionArgs);
break ;
case STUS:
rowCount = db.delete( "student" , selection, selectionArgs);
default : break ;
}
return rowCount;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
int flag = URI_MATCHER.match(uri);
int count = - 1 ;
SQLiteDatabase db = stuDbHepler.getWritableDatabase();
String whereClause = null ;
switch (flag) {
case STU:
whereClause = "id = " + ContentUris.parseId(uri);
if (selection != null && selection.equals( "" )) {
whereClause += " and " + selection ;
}
count = db.update( "student" , values, whereClause, selectionArgs);
break ;
case STUS:
count = db.update( "student" , values, selection, selectionArgs);
break ;
}
return count;
} } |
最后,ContentProvider也是安卓的四大组件之一,所以我们要在Manifest文件的application标签中对其进行注册。这样,一个内容提供者就建立好了。我们可以另外建立一个测试工程,对其进行跨进程访问,不过需要注意的是我们对其进行访问前必须确定该ContentProvider是存在的,所以必须先运行ContentProvider的工程,然后就可以使用测试工程对其进行访问了。这里需要注意的是,当我们把ContentProvider进程关闭后,我们还是可以对其数据进行访问的。一下是测试工程的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
import android.content.ContentResolver; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import android.test.AndroidTestCase; import android.util.Log; /*
* 新建一个工程,建立一个测试类来测试是否能够跨进程访问数据。分别实现增删改查四种方法。
* */ public class TestCP extends AndroidTestCase {
public void insertData() {
ContentResolver cr = getContext().getContentResolver();
ContentValues values = new ContentValues();
values.put( "id" , 3 );
values.put( "name" , "Jiky" );
values.put( "age" , 18 );
Uri resultUri = cr.insert(uri, values);
if (resultUri != null ) {
Log.i( "cth" ,resultUri.toString());
} else {
Log.e( "cth" , "插入失败" );
}
}
public void deleteData() {
ContentResolver cr = getContext().getContentResolver();
String where = "id = ?" ;
int deleteRowNum = cr.delete(uri, where, new String[] { "123" });
Log.i( "cth" , "deleteRowNum is " + deleteRowNum );
}
public void updateData() {
ContentResolver cr = getContext().getContentResolver();
Uri uri = Uri.parse( "content://cth.android.contentprovider.StuDbCP/student/" ); //直接把要修改的id加在PATH后,也可按一下方式。
ContentValues values = new ContentValues();
values.put( "name" , "Mike" );
values.put( "age" , 11 );
int rowId = cr.update(uri, values, "id = ?" , new String[]{ "1" });
if (rowId == 0 ) {
Log.e( "cth" , "找不到匹配项。" );
} else {
Log.i( "cth" , "rowId = " + rowId);
}
}
public void selectData() {
ContentResolver cr = getContext().getContentResolver();
Cursor cursor = cr.query(uri, new String[]{ "name" , "id" }, null , null , null );
while (cursor.moveToNext()) {
Log.i( "cth" ,cursor.getString(cursor.getColumnIndex( "id" )) + " count = " + cursor.getCount());
}
} } |
解读ContentResolver和ContentProvider的更多相关文章
- ContentResolver + SqliteOpenHelper + ContentProvider 理解
惭愧,现在才接触到ContentResolver的用法 这个类主要是Android用来实现应用程序之间数据共享的 一个应用程序可以将自己的数据完全暴露出去,外界更本看不到,也不用看到这个应用程序暴露的 ...
- ContentProvider与ContentResolver使用【转】
这篇文章被转载而转载者未注明原文出处,在此未加上原文地址链接,本人向原作者致以歉意. 下面是文章内容: 使用ContentProvider共享数据: 当应用继承ContentProvider类,并重写 ...
- ContentProvider与ContentResolver使用
例如以下内容为从网络转载: 使用ContentProvider共享数据: 当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就能够向其它应用共享其数据.虽然使用其它方 ...
- ContentProvider和ContentResolver的使用
ContentProvider ContentProvider 在android中的作用是对外共享数据,也就是说你可以通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通 ...
- ContentProvider与ContentResolver
使用ContentProvider共享数据: 当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据.虽然使用其他方法也可以对外共享数据,但数据 ...
- ContentProvider中央档案馆,以及获取联系人电话的示例
Android官方文档介绍的数据存储方式共有五种,sqlite,SharedPreferences,网络存储,外储存储,文件存储,但是这些数据都无法进行共享,那么我们就引入了今天的主角:Content ...
- Android四大组件--ContentProvider详解(转)
一.相关ContentProvider概念解析: 1.ContentProvider简介在Android官方指出的Android的数据存储方式总共有五种,分别是:Shared Preferences. ...
- ContentProvider数据访问详解
ContentProvider数据访问详解 Android官方指出的数据存储方式总共有五种:Shared Preferences.网络存储.文件存储.外储存储.SQLite,这些存储方式一般都只是在一 ...
- ContentProvider总结
一.使用ContentProvider(内容提供者)共享数据 ContentProvider在android中的作用是对外共享数据,也就是说你可以通过ContentProvider把应用中的数据共享给 ...
随机推荐
- Android自动化测试-自动获取cpu和内存信息
CpuInfo.java package com.dtest; import java.io.BufferedReader; import java.io.FileWriter; import jav ...
- Android 自动化测试—robotium(十一) robotium实现微博绑定
利用robotium实现微博的自动绑定 android应用进行微博绑定主要有两种方式,一种客户端实现个webView,请求微博默认的授权页,另一种则是直接调用微博客户端,这里实现的为前一种,即进和微博 ...
- 创建WSDL项目
WSDL文件是测试基于soap的服务,他们定义实际暴露服务和要求SoapUI生成测试,要求信息,验证和MockServices. SoapUI支持最广泛使用的1.1版本的WSDL和SOAP 1.1和1 ...
- Python for Informatics 第11章 正则表达式五(译)
注:文章原文为Dr. Charles Severance 的 <Python for Informatics>.文中代码用3.4版改写,并在本机测试通过. 11.4 转义字符 之前我们在正 ...
- [机器学习] 虚拟机VMware中使用Ubuntu的联网问题
在VMware中安装Ubuntu要解决两个问题: 1.VMware Tools安装使用 2.Ubuntu联网的虚拟机设置 1.VMware Tools安装 它的作用就是使用户可以从物理主机直接往虚拟机 ...
- 规约模式Specification的学习
最近一直在看DDD开发 规约似乎用得很普遍. 但是还是理解不了.所以记录下学习的进度.- 规约(Specification)模式 目的:查询语句和查询条件的分离 写了一个关于规约的模拟小程序 cla ...
- 如何打开VPK文件?里面究竟有什么?
在DOTA2游戏目录下有一类VPK文件,如何打开VPK文件? 下载http://nemesis.thewavelength.net/index.php?p=26,安装完成后即可打开. 打开dota 2 ...
- Android-adb指令
adb概念: adb的全称为Android Debug Bridge(调试桥):通过adb我们可以在Eclipse中方便通过DDMS来调试Android程序.当我们运行Eclipse时ADB进程 ...
- 【30集iCore3_ADP出厂源代码(ARM部分)讲解视频】30-1 前言
视频简介: 该视频介绍收到iCore3应用开发平台后如何获取出厂代码,以 及如何下载出厂代码到应用开发平台中. 源视频包下载地址: http://pan.baidu.com/s/1nuUZW17 ...
- Windows自带压缩解压工具
压缩一个文件: 命令行:makecab fileName.txt fileName.zip 鼠标操作:选中文件-->鼠标右键-->Send to-->Compressed (zipp ...