ContentProvider

ContentProvider简介

ContentProvider(数据提供者)是在应用程序间共享数据的一种接口机制
ContentProvider提供了更为高级的数据共享方法,应用程序可以指定需要共享的数据,而其他应用程序则可以在不知数据来源、路径的情况下,对共享数据进行查询、添加、删除和更新等操作
许多Android系统的内置数据也通过ContentProvider提供给用户使用,例如通讯录、音视频文件和图像文件等
在创建ContentProvider时,需要首先使用数据库、文件系统或网络实现底层存储功能,然后在继承ContentProvider的类中实现基本数据操作的接口函数,包括添加、删除、查找和更新等功能
调用者不能够直接调用ContentProvider的接口函数,而需要使用ContentResolver对象,通过URI间接调用ContentProvider。

使用ContentProvider可以在不同的应用程序之间共享数据。 它为存储和获取数据提供了统一的接口。ContentProvide对数据进行封装,不用关心数据存储的细节。

ContentProvider不管底层数据的实际存储方式,对外统一使用表的形式来组织数据 。

URI的简介

Uri代表了要操作的数据,它为系统的每一个资源给其一个名字,比方说通话记录。每一个ContentProvider都拥有一个公共的URI,这个URI用于表示这个ContentProvider所提供的数据。 

 URI的格式

A:标准前缀,用来说明一个Content Provider控制这些数据,无法改变的;"content://"
 B:URI 的标识,它定义了是哪个Content Provider提供这些数据。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的、小写的类名。这个标识在元素的 authorities属性中说明:一般是定义该ContentProvider的包.类的名称; "content://hx.android.text.myprovider"
C:路径,通俗的讲就是你要操作的数据库中表的名字;"content://hx.android.text.myprovider/tablename"
D:如果URI中包含表示需要获取的记录的ID;则就返回该id对应的数据,如果没有ID,就表示返回全部; "content://hx.android.text.myprovider/tablename/#" #表示数据id

路径(path):可以用来表示我们要操作的数据,路径的构建应根据业务而定,如下: • 要操作contact表中id为10的记录,可以构建这样的路径:/contact/10 • 要操作contact表中id为10的记录的name字段, contact/10/name • 要操作contact表中的所有记录,可以构建这样的路径:/contact
要操作的数据不一定来自数据库,也可以是文件等他存储方式,如下: 要操作xml文件中contact节点下的name节点,可以构建这样的路径:/contact/name
如果要把一个字符串转换成Uri,可以使用Uri类中的parse()方法,如下: Uri uri = Uri.parse("content://com.changcheng.provider.contactprovider/contact")

UriMatcher

因为Uri代表了要操作的数据,所以我们很经常需要解析Uri,并从Uri中获取数据。Android系统提供UriMatcher ,用于匹配Uri,用法如下:   
1.首先把你需要匹配Uri路径全部给注册上,如:
   uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact”, 1);
   uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact/#”, 2);
2.注册完需要匹配的Uri后,就可以使用uriMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码.
//如果match()匹配路径,返回匹配码为1
 content://com.changcheng.sqlite.provider.contactprovider/contact
//如果match()匹配路径,返回匹配码为2
content://com.changcheng.sqlite.provider.contactprovider/contact/23

因为Uri代表了要操作的数据,所以我们经常需要解析Uri,并从Uri中获取数据。Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher 和ContentUris 。掌握它们的使用,会便于我们的开发工作。
UriMatcher类用于匹配Uri,它的用法如下:
首先第一步把你需要匹配Uri路径全部给注册上,如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
UriMatcher  sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配content://cn.itcast.provider.personprovider/person路径,返回匹配码为1
sMatcher.addURI(“cn.itcast.provider.personprovider”, “person”, 1);//添加需要匹配uri,如果匹配就会返回匹配码
//如果match()方法匹配content://cn.itcast.provider.personprovider/person/230路径,返回匹配码为2
sMatcher.addURI(“cn.itcast.provider.personprovider”, “person/#”, 2);//#号为通配符
switch (sMatcher.match(Uri.parse("content://cn.itcast.provider.personprovider/person/10"))) {
   case 1
    break;
   case 2
    break;
   default://不匹配
    break;
}
注册完需要匹配的Uri后,就可以使用sMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用addURI()方法传入的第三个参数,假设匹配content://cn.itcast.provider.personprovider/person路径,返回的匹配码为1

 ContentUris类

ContentUris类用于获取Uri路径后面的ID部分,它有两个比较实用的方法:
withAppendedId(uri, id)用于为路径加上ID部分:
Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person")
Uri resultUri = ContentUris.withAppendedId(uri, 10);
//生成后的Uri为:content://cn.itcast.provider.personprovider/person/10

parseId(uri)方法用于从路径中获取ID部分:
Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person/10")
long personid = ContentUris.parseId(uri);//获取的结果为:10

ContentProvider的编程方法

程序开发人员通过继承ContentProvider类可以创建一个新的数据提供者,过程可以分为三步
1.继承ContentProvider,并重载六个函数
public boolean onCreate()
该方法在ContentProvider创建后就会被调用, Android开机后, 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)
该方法用于返回当前Url所代表数据的MIME类型。如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,例如:要得到所有person记录的Uri为content://cn.itcast.provider.personprovider/person,那么返回的MIME类型字符串应该为:“vnd.android.cursor.dir/person”。如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,例如:得到id为10的person记录,Uri为content://cn.itcast.provider.personprovider/person/10,那么返回的MIME类型字符串应该为:“vnd.android.cursor.item/person”。

2.声明CONTENT_URI,实现UriMatcher
3.注册ContentProvider  AndroidManifest.xml使用<provider>对该ContentProvider进行配置,为了能让其他应用找到该ContentProvider , ContentProvider 采用了authorities(主机名/域名)对它进行唯一标识,你可以把 ContentProvider看作是一个网站(想想,网站也是提供数据者),authorities 就是他的域名:

<application android:icon="@drawable/icon" android:label="@string/app_name">
 <provider android:name = ".PeopleProvider"
   android:authorities = "edu.hrbeu.peopleprovider"/>
</application>

注意:一旦应用继承了ContentProvider类,后面我们就会把这个应用称为ContentProvider(内容提供者)。

ContentResolver的编程方法

使用ContentProvider是通过Android组件都具有的ContentResolver对象,通过URI进行数据操作
程序开发人员只需要知道URI和数据集的数据格式,则可以进行数据操作,解决不同应用程序之间的数据共享问题
每个Android组件都具有一个ContentResolver对象,获取ContentResolver对象的方法是调用getContentResolver()函数

使用ContentResolver操作ContentProvider中的数据

当外部应用需要对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和对其中的什么数据进行操作,假设给定的是: Uri.parse(“content://cn.itcast.providers.personprovider/person/10”),那么将会对主机名为cn.itcast.providers.personprovider的ContentProvider进行操作,操作的数据为person表中id为10的记录。

使用ContentResolver对ContentProvider中的数据进行添加、删除、修改和查询操作
ContentResolver resolver =  getContentResolver();
Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person");
//添加一条记录
ContentValues values = new ContentValues();
values.put("name", "itcast");
values.put("age", 25);
resolver.insert(uri, values);  
//获取person表中所有记录
Cursor cursor = resolver.query(uri, null, null, null, "personid desc");
while(cursor.moveToNext()){
 Log.i("ContentTest", "personid="+ cursor.getInt(0)+ ",name="+ cursor.getString(1));
}
//把id为1的记录的name字段值更改新为liming
ContentValues updateValues = new ContentValues();
updateValues.put("name", "liming");
Uri updateIdUri = ContentUris.withAppendedId(uri, 2);
resolver.update(updateIdUri, updateValues, null, null);
//删除id为2的记录
Uri deleteIdUri = ContentUris.withAppendedId(uri, 2);
resolver.delete(deleteIdUri, null, null);

当ContentProvider中的数据发生变化时可以向其用户发出通知

如果ContentProvider的访问者需要得知ContentProvider中的数据发生了变化,可以在ContentProvider 发生数据变化时调用getContentResolver().notifyChange(uri, null)来通知注册在此URI上的访问者,例子如下:
public class PersonContentProvider extends ContentProvider {
public Uri insert(Uri uri, ContentValues values) {
 db.insert("person", "personid", values);
 getContext().getContentResolver().notifyChange(uri, null);
}
}
如果ContentProvider的访问者需要得到数据变化通知,必须使用ContentObserver对数据(数据采用uri描述)进行监听,当监听到数据变化通知时,系统就会调用ContentObserver的onChange()方法:
getContentResolver().registerContentObserver(Uri.parse("content://cn.itcast.providers.personprovider/person"),
          true, new PersonObserver(new Handler()));
public class PersonObserver extends ContentObserver{
 public PersonObserver(Handler handler) {
  super(handler);
  }
 public void onChange(boolean selfChange) {
     //此处可以进行相应的业务处理
 }
}

具体一个例子代码:

View Code
  1 import cn.itcast.service.DBOpenHelper;
2 import android.content.ContentProvider;
3 import android.content.ContentUris;
4 import android.content.ContentValues;
5 import android.content.UriMatcher;
6 import android.database.Cursor;
7 import android.database.sqlite.SQLiteDatabase;
8 import android.net.Uri;
9
10 public class PersonProvider extends ContentProvider {
11 private DBOpenHelper dbOpenHelper;
12 //常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
13 private static final UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
14 private static final int PERSONS = 1;
15 private static final int PERSON = 2;
16 static{
17 //如果match()方法匹配content://cn.itcast.provider.personprovider/person路径,返回匹配码为1
18 MATCHER.addURI("cn.itcast.providers.personprovider", "person", PERSONS);
19 //如果match()方法匹配content://cn.itcast.provider.personprovider/person/230路径,返回匹配码为2
20 MATCHER.addURI("cn.itcast.providers.personprovider", "person/#", PERSON);
21 }
22 //删除person表中的所有记录 /person
23 //删除person表中指定id的记录 /person/10
24 @Override
25 public int delete(Uri uri, String selection, String[] selectionArgs) {
26 SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
27 int count = 0;
28 switch (MATCHER.match(uri)) {
29 case PERSONS:
30 count = db.delete("person", selection, selectionArgs);
31 return count;
32
33 case PERSON:
34 long id = ContentUris.parseId(uri);
35 String where = "personid="+ id;
36 if(selection!=null && !"".equals(selection)){
37 where = selection + " and " + where;
38 }
39 count = db.delete("person", where, selectionArgs);
40 return count;
41
42 default:
43 throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());
44 }
45 }
46
47 @Override
48 public String getType(Uri uri) {//返回当前操作的数据的mimeType
49 switch (MATCHER.match(uri)) {
50 case PERSONS: //多条数据
51 return "vnd.android.cursor.dir/person";
52
53 case PERSON: //单条数据
54 return "vnd.android.cursor.item/person";
55
56 default:
57 throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());
58 }
59 }
60
61 @Override
62 public Uri insert(Uri uri, ContentValues values) {// /person
63 SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
64 switch (MATCHER.match(uri)) {
65 case PERSONS:
66 long rowid = db.insert("person", "name", values);
67 //生成后的Uri为:content://cn.itcast.provider.personprovider/person/10
68 Uri insertUri = ContentUris.withAppendedId(uri, rowid);//得到代表新增记录的Uri
69 //当ContentProvider中的数据发生变化时可以向其用户发出通知,第一个参数为uri,说明是person表的uri,不是单条记录的uri
70 this.getContext().getContentResolver().notifyChange(uri, null);
71 return insertUri;
72
73 default://不匹配
74 throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());
75 }
76 }
77
78 @Override
79 public boolean onCreate() {
80 this.dbOpenHelper = new DBOpenHelper(this.getContext());
81 return false;
82 }
83 //查询person表中的所有记录 /person
84 //查询person表中指定id的记录 /person/10
85 @Override
86 public Cursor query(Uri uri, String[] projection, String selection,
87 String[] selectionArgs, String sortOrder) {
88 SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
89 switch (MATCHER.match(uri)) {
90 case PERSONS:
91 return db.query("person", projection, selection, selectionArgs, null, null, sortOrder);
92
93 case PERSON:
94 long id = ContentUris.parseId(uri);
95 String where = "personid="+ id;
96 if(selection!=null && !"".equals(selection)){
97 where = selection + " and " + where;
98 }
99 return db.query("person", projection, where, selectionArgs, null, null, sortOrder);
100
101 default:
102 throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());
103 }
104 }
105
106 //更新person表中的所有记录 /person
107 //更新person表中指定id的记录 /person/10
108 @Override
109 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
110 SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
111 int count = 0;
112 switch (MATCHER.match(uri)) {
113 case PERSONS:
114 count = db.update("person", values, selection, selectionArgs);
115 return count;
116
117 case PERSON:
118 //parseId(uri)方法用于从路径中获取ID部分:获取的结果为:10
119 long id = ContentUris.parseId(uri);
120 String where = "personid="+ id;
121 //如果外面传进来的条件不为空,而且不为空字符串
122 if(selection!=null && !"".equals(selection)){
123 //外面的条件加自己的条件
124 where = selection + " and " + where;
125 }
126 count = db.update("person", values, where, selectionArgs);
127 return count;
128
129 default:
130 throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());
131 }
132 }
133
134 }

AndroidMainFest.xml代码

View Code
 1 <?xml version="1.0" encoding="utf-8"?>
2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3 package="cn.itcast.db"
4 android:versionCode="1"
5 android:versionName="1.0">
6 <application android:icon="@drawable/icon" android:label="@string/app_name">
7 <uses-library android:name="android.test.runner" />
8 <activity android:name=".MainActivity"
9 android:label="@string/app_name">
10 <intent-filter>
11 <action android:name="android.intent.action.MAIN" />
12 <category android:name="android.intent.category.LAUNCHER" />
13 </intent-filter>
14 </activity>
15 <provider android:name=".PersonProvider" android:authorities="cn.itcast.providers.personprovider"/>
16 </application>
17 <uses-sdk android:minSdkVersion="8" />
18 <instrumentation android:name="android.test.InstrumentationTestRunner"
19 android:targetPackage="cn.itcast.db" android:label="Tests for My App" />
20 </manifest>

另一个应用程序调用ContentProvider

View Code
 1 import android.content.ContentResolver;
2 import android.content.ContentValues;
3 import android.database.Cursor;
4 import android.net.Uri;
5 import android.test.AndroidTestCase;
6 import android.util.Log;
7
8 public class AccessContentProviderTest extends AndroidTestCase {
9 private static final String TAG = "AccessContentProviderTest";
10
11 /**
12 * 往内容提供者添加数据
13 * @throws Throwable
14 */
15 public void testInsert() throws Throwable{
16 ContentResolver contentResolver = this.getContext().getContentResolver();
17 Uri insertUri = Uri.parse("content://cn.itcast.providers.personprovider/person");
18 ContentValues values = new ContentValues();
19 values.put("name", "zhangxiaoxiao");
20 values.put("amount", 90);
21 Uri uri = contentResolver.insert(insertUri, values);
22 Log.i(TAG, uri.toString());
23 }
24
25 /**
26 * 更新内容提供者中的数据
27 * @throws Throwable
28 */
29 public void testUpdate() throws Throwable{
30 ContentResolver contentResolver = this.getContext().getContentResolver();
31 Uri updateUri = Uri.parse("content://cn.itcast.providers.personprovider/person/1");
32 ContentValues values = new ContentValues();
33 values.put("name", "lili");
34 contentResolver.update(updateUri, values, null, null);
35 }
36
37 /**
38 * 从内容提供者中删除数据
39 * @throws Throwable
40 */
41 public void testDelete() throws Throwable{
42 ContentResolver contentResolver = this.getContext().getContentResolver();
43 Uri deleteUri = Uri.parse("content://cn.itcast.providers.personprovider/person/1");
44 contentResolver.delete(deleteUri, null, null);
45 }
46
47 /**
48 * 获取内容提供者中的数据
49 * @throws Throwable
50 */
51 public void testFind() throws Throwable{
52 ContentResolver contentResolver = this.getContext().getContentResolver();
53 Uri selectUri = Uri.parse("content://cn.itcast.providers.personprovider/person");
54 Cursor cursor = contentResolver.query(selectUri, null, null, null, "personid desc");
55 while(cursor.moveToNext()){
56 int id = cursor.getInt(cursor.getColumnIndex("personid"));
57 String name = cursor.getString(cursor.getColumnIndex("name"));
58 int amount = cursor.getInt(cursor.getColumnIndex("amount"));
59 Log.i(TAG, "id="+ id + ",name="+ name+ ",amount="+ amount);
60 }
61 }
62 }


这个app潮流公众帐号主要是推荐给手机用户最近最潮的软件,让大家随时跟上时尚。我们会提供给你们最好的服务,喜欢我们就帮我们推荐吧!

原文地址:https://www.cnblogs.com/shaoyangjiang/p/2404010.html