ContentResolver和ContentProvider
本文转自http://cthhqu.blog.51cto.com/7598297/1281217
1. ContentProvider的概述
ContentProvider:
(Official Definition)Content providers manage access to a structured set of data. They encapsulate the data, and provide mechanisms
for defining data security. Content providers are the standard interface that connects data in one process with code running in another process.
由官方的定义我们可以得知它是一个管理访问结构化数据的机制。我们系统中有些数据很重要,不能让人随便访问,但是因为比较有价值,所以很多应用程序需要用到它,这是就可通过ContentProvider这个机制,压缩数据,提供安全定义、访问数据的机制。该机制提供一个借口,使得应用程序能从该进程访问另外一个应用程序的数据。
不仅是系统重要的数据,如果我们开发过程中有数据也是比较重要,但是需要提供给多个程序访问,这个时候也可以用到ContentProvider机制,把我们的数据的增删改查分装在ContentProvider的增删改查中,并增加相应的安全机制,使得用户可我们规定的安全机制下访问我们的数据。
ContentProvider还有一个重要的特点就是它是可以使得某些数据可以被跨进程访问,一般我们的数据库是不可跨进程被访问,因为数据库一般的数据是属于某个应用程序的,如果其他程序可以随意访问其数据库,这是很危险的,但是如果该应用程序的数据想分享给其他应用程序,那么就可以通过建立一个ContentProvider,规定一些安全机制,屏蔽一些比较重要的数据被访问,或是规定访问权限,比如只可读不可写等,使其他应用程序在有限制的前提下访问我们的数据。这样做就会起到对数据进行保护同时又能使得有用的数据能被分享的作用。
2. ContentResolver和ContentProvider的使用方法
2.1 ContentResolver的使用方法:
首先,我们得了解如何通过ContentResolver来对系统或我们自定义的ContentProvider进行交互,通常,我们常用到的是访问系统的数据,常见的有通讯录、日历、短信、多媒体等,下面以通过ContentResolver获取系统的通讯录为例子,简单演示是如何使用ContentResolver的:
使用方法归纳:
1、从当前Activity获取系统的ContentResolver;
2、使用ContentProvider的insert、delete、update、query方法对ContentProvider的内容进行增删改查;
3、如果是使用query是的到一个Cursor的结果集,通过该结果集可以获得我们查询的结果。
源代码示例:
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
} } |
2.2 常用的系统URI
访问系统提供的其他数据方法基本类似,只是传入的URI有所区别,这些URI都是API里面提供的,通过系统规定的不同URI可访问系统不同的数据。系统常用的URI如下:
2.3 ContentProvider的使用方法:
那么我们是如何自定义ContentProvider来对其他程序提供访问我们数据的接口的呢?
一般我们如果有数据想被跨进程共享,就可以定义个ContentProvider,并在该ContentProvider定义访问该数据的方法,这些方法只允许外界传入一个URI和其他附加信息,该URI用于定位想操作数据的对象,而附加信息一般就是操作对象里具体数据的条件(例如SQL里面的where语句或者是SharedPreferences的键值对)。这样,就能做到对外界屏蔽了访问数据的方法,单纯开发URI和附加信息的接口,使得其他应用程序是在我们规定的机制下访问数据。这样既能做到跨进程数据共享,又能消除访问不同类型数据时访问方法的差异性,用户可不用管访问的数据类型是数据库或是其他类型而单纯用URI和附加信息定位操作数据对象,同时有提高了安全性。
2.3.1 URI简介:
安卓中系统和用户自定义ContentProvider不止一个,那么当我们想访问某个ContentProvider时,我们是怎么定位到的呢?由上文可知我们是通过URI定位到我们具体想访问的数据,这时我们得先了解用于定位ContentProvider的URI的构成以及各部分的意义。官网API文档是这样介绍的:
Content URIs have the syntax
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.
|
Add a URI to match, and the code to return when this URI is matched.
|
Public Methods | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
|
Appends the given ID to the end of the path.
|
|
|
|
|
|
|
|
|
|
|
|
Converts the last path segment to a long.
|
|
|
|
|
|
|
|
|
|
|
|
Appends the given ID to the end of the path.
|
|
|
|
|
|
|
|
|
|
|
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)
{
} } |
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;
}
} |
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());
}
} }
|