5.4、Android中数据存储和访问-使用ContentProvider共享数据

使用ContentProvider共享数据

当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。以前我们学习过文件的操作模式,通过指定文件的操作模式为Context.MODE_WORLD_READABLE Context.MODE_WORLD_WRITEABLE同样可以对外共享数据,但数据的访问方式会因数据存储的方式而不同,如:采用xml文件对外共享数据,需要进行xml解析来读写数据;采用sharedpreferences共享数据,需要使用sharedpreferences API读写数据。而使用ContentProvider共享数据的好处是统一了数据访问方式。

当应用需要通过ContentProvider对外共享数据时,第一步需要继承ContentProvider并重写下面方法:

public class PersonContentProvider extends ContentProvider{

   public boolean onCreate()

   public Uri insert(Uri uri, ContentValues values)

   public int delete(Uri uri, String selection, String[] selectionArgs)

   public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)

   public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)

   public String getType(Uri uri)}

第二步需要在AndroidManifest.xml使用<provider>对该ContentProvider进行配置,为了能让其他应用找到该ContentProvider , ContentProvider 采用了authorities(主机名/域名)对它进行唯一标识,你可以把 ContentProvider看作是一个网站(想想,网站也是提供数据者),authorities 就是他的域名:

<manifest .... >

    <application android:icon="@drawable/icon" android:label="@string/app_name">

        <provider android:name=".PersonContentProvider" android:authorities="cn.itcast.providers.personprovider"/>

    </application>

</manifest>

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

package cn.itcast.db;

import cn.itcast.service.DBOpenHelper;

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 PersonContentProvider extends ContentProvider {

private static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);

private static final int PERSONS = 1;

private static final int PERSON = 2;

private DBOpenHelper dbOpenHelper;

static{

matcher.addURI("cn.itcast.providers.personprovider", "person", PERSONS);

matcher.addURI("cn.itcast.providers.personprovider", "person/#", PERSON);

}

@Override

public boolean onCreate() {

dbOpenHelper = new DBOpenHelper(this.getContext());

return true;

}

@Override

public int delete(Uri uri, String selection, String[] selectionArgs) {

SQLiteDatabase db = dbOpenHelper.getWritableDatabase();

int num = 0 ;//已经删除的记录数量

switch (matcher.match(uri)) {

case PERSONS:

num = db.delete("person", selection, selectionArgs);

break;

case PERSON:

long id = ContentUris.parseId(uri);

String where = "personid="+ id;

if(selection!=null && !"".equals(selection)){ // personid=12 and name=?

where = where + " and "+ selection;

}

num = db.delete("person", where, selectionArgs);

break;

default:

throw new IllegalArgumentException("Unkown Uri:"+ uri);

}

getContext().getContentResolver().notifyChange(uri, null);

return num;

}

@Override

public String getType(Uri uri) {//返回当前操作的数据类型

switch (matcher.match(uri)) {

case PERSONS://操作的是集合类型数据

return "vnd.android.cursor.dir/person";

case PERSON:

return "vnd.android.cursor.item/person";

default:

throw new IllegalArgumentException("Unkown Uri:"+ uri);

}

}

@Override

public Uri insert(Uri uri, ContentValues values) {

SQLiteDatabase db = dbOpenHelper.getWritableDatabase();

long id = 0 ;

switch (matcher.match(uri)) {

case PERSONS:

id = db.insert("person", "personid", values);//得到记录的id

getContext().getContentResolver().notifyChange(uri, null);

return ContentUris.withAppendedId(uri, id);//返回代表新增记录的Uri

case PERSON:

id = db.insert("person", "personid", values);//得到记录的id

String strUri = uri.toString();

Uri personUri = Uri.parse(strUri.substring(0, strUri.lastIndexOf("/")));

getContext().getContentResolver().notifyChange(personUri, null);

return ContentUris.withAppendedId(personUri, id);

default:

throw new IllegalArgumentException("Unkown Uri:"+ uri);

}

}

@Override

public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {

SQLiteDatabase db = dbOpenHelper.getReadableDatabase();

switch (matcher.match(uri)) {

case PERSONS:

return db.query("person", projection, selection, selectionArgs, null, null, sortOrder);

case PERSON:

long id = ContentUris.parseId(uri);

String where = "personid="+ id;

if(selection!=null && !"".equals(selection)){ // personid=12 and name=?

where = where + " and "+ selection;

}

return db.query("person", projection, where, selectionArgs, null, null, sortOrder);

default:

throw new IllegalArgumentException("Unkown Uri:"+ uri);

}

}

@Override

public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {

SQLiteDatabase db = dbOpenHelper.getWritableDatabase();

int num = 0 ;//已经修改的记录数量

switch (matcher.match(uri)) {

case PERSONS:

num = db.update("person", values, selection, selectionArgs);

break;

case PERSON:

long id = ContentUris.parseId(uri);

String where = "personid="+ id;

if(selection!=null && !"".equals(selection)){ 

where = where + " and "+ selection;

}

num = db.update("person", values, where, selectionArgs);

break;

default:

throw new IllegalArgumentException("Unkown Uri:"+ uri);

}

getContext().getContentResolver().notifyChange(uri, null);//通知数据发生变化

return num;

}

}

Uri介绍

Uri代表了要操作的数据,Uri主要包含了两部分信息:

1》需要操作的ContentProvider 

2》对ContentProvider中的什么数据进行操作,一个Uri由以下几部分组成:

ContentProvider(内容提供者)的scheme已经由Android所规定, scheme为:content://

主机名(或叫Authority)用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。

路径(path)可以用来表示我们要操作的数据,路径的构建应根据业务而定,如下:

要操作person表中id10的记录,可以构建这样的路径:/person/10

要操作person表中id10的记录的name字段, person/10/name

要操作person表中的所有记录,可以构建这样的路径:/person

要操作xxx表中的记录,可以构建这样的路径:/xxx

当然要操作的数据不一定来自数据库,也可以是文件、xml或网络等其他存储方式,如下:

要操作xml文件中person节点下的name节点,可以构建这样的路径:/person/name

如果要把一个字符串转换成Uri,可以使用Uri类中的parse()方法,如下:

Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person")



 UriMatcher类使用介绍

因为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类用于获取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类主要方法的作用:

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记录的Uricontent://cn.itcast.provider.personprovider/person,那么返回的MIME类型字符串应该为:“vnd.android.cursor.dir/person”。如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,例如:得到id10person记录,Uricontent://cn.itcast.provider.personprovider/person/10,那么返回的MIME类型字符串应该为:“vnd.android.cursor.item/person”

使用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.personproviderContentProvider进行操作,操作的数据为person表中id10的记录。

使用ContentResolverContentProvider中的数据进行添加、删除、修改和查询操作:

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));

}

//id1的记录的name字段值更改新为liming

ContentValues updateValues = new ContentValues();

updateValues.put("name", "liming");

Uri updateIdUri = ContentUris.withAppendedId(uri, 2);

resolver.update(updateIdUri, updateValues, null, null);

//删除id2的记录

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描述)进行监听,当监听到数据变化通知时,系统就会调用ContentObserveronChange()方法:

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) {

    //此处可以进行相应的业务处理

}

}




content provider  

2010-01-09 18:42:07|  分类: android|字号 订阅

重要的类:

l ContentProvider:内容提供者实现此类的派生类,以统一接口提供共享数据信息。

l ContentResolver:内容读取者使用此类来获取其他应用程序的信息

Android的应用程序使用Content Provider来存储或读取数据,这是Android下应用程序之间共享数据的唯一方式。

如果你像让其他应用程序共享你的数据,有两个选择:创建自己的content provider(ContentProvider的子类)或者把你的数据添加到已经存在的provider中。

如何保存数据是app依赖于设计者,但是所有的内容提供者实现了一个通用的接口用来查询提供的内容并且返回结果,当然还有添加、更改和删除数据。



使用方法:

在Activity中:

ContentResolver cr = getContentResolver();

然后就可以使用ContentResolver的方法来操作provider提供的数据。



Android系统负责初始化所有的provider,用户不需要直接和ContentProvider对象打交道。正常情况下每一种ContentProvider在系统中有一个实例,但是它可以和多个不同app的ContentResolver交互。

数据模型

使用的类Cursor;

Content Provider对外显示其数据像一个简单的表,每行是一个记录(record),每列式一个类型的数据,就像最简单的数据库里的table那样。不同之处是每个记录必须有一个唯一的_ID来标识它。

一个查询操作(query)返回一个Cursor对象,使用此对象,用户可以逐记录或逐列来读取table中的各个field。

URI



所有ContentResolver方法的第一个参数都是URI,它表示

每个ContentProvider向外提供一个URI(由Uri对象进行封装)用来表示自己提供的数据集,当提供多个数据表(数据集)时使用多个URI来表示每个数据表。所有的URI都以“content://”开头。

Android对系统提供的所有content provider定义了单独的URI,比如对于电话号码和图片的数据表的URI:

android.provider.Contacts.Phones.CONTENT_URI 
android.provider.Contacts.Photos.CONTENT_URI

最近拨打的电话和日历:

android.provider.CallLog.Calls.CONTENT_URI 
android.provider.Calendar.CONTENT_URI 



查询ContentProvider

查询content provider时需要3类新星:

1. 指定内容提供着的URI

2. 在数据表中数据域的名字(即那一列)

3. 数据类型

执行一个查询

要对content provider执行查询,可以使用ContentResolver.query()或Activity.managedQuery()。这两个函数的参数完全相同,不同之处是谁负责返回的Cursor的生命周期。

这两个函数的第一个参数是URI。使用URI时可用指名数据报的_ID来返回特定数据项的信息。

可以使用类似“conent://…../23”(for _ID=23)形式的URI。为了使添加ID的操作更加方便,类Uri提供了两个静态方法:

l Uri.withAppendedId(URI, ID)

l Uri.withAppendedPath(URI, “xxx”);



函数原型及参数

public final Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) 

Query the given URI, returning a Cursor over the result set.

Parameters
uri 
The URI, using the content:// scheme, for the content to retrieve.

Projection[column] 
A list of which columns to return. Passing null will return all columns, which is discouraged to prevent reading da ta from storage that isn't going to be used.

Selection(row) 
A filter declaring which rows to return, formatted as an SQL WHERE clause (excluding the WHERE itself). Passing null will return all rows for the given URI.

selectionArgs 
You may include ?s in selection, which will be replaced by the values from selectionArgs, in the order that they appear in the selection. The values will be bound as Strings.

sortOrder 
How to order the rows, formatted as an SQL ORDER BY clause (excluding the ORDER BY itself). Passing null will use the default sort order, which may be unordered.




读取返回的数据

Cursor对象有一系列独立的方法来读取不同类型的数据,比如getString()/getInt()/getFloat()

imp ort android.provider.Contacts.People;

private void getColumnData(Cursor cur){ 

if (cur.moveToFirst()) {

String name; 

String phoneNumber; 

int nameColumn = cur.getColumnIndex(People.NAME); 

int phoneColumn = cur.getColumnIndex(People.NUMBER);

String imagePath; 

do {

// Get the field values

name = cur.getString(nameColumn);

phoneNumber = cur.getString(phoneColumn);

// Do something with the values. 

... 

} while (cur.moveToNext());

}

}

操作数据的方法

l 添加新纪录

ContentValues values = new ContentValues();

values.put(People.NAME, "Abraham Lincoln");

values.put(People.STARRED, 1);

Uri uri = getContentResolver().insert(People.CONTENT_URI, values);

l 对已存在的纪录添加信的值

更新URI(在原URI后边添加新的值,用来表示新添加的column的名字),通过getContentResolver().insert(new-uri, values);

l 批处理更新已存在的纪录

ContentResolver.update()

l 删除记录

ContentResolver.delete()



创建Content Provider

l Set up a system for storing the da ta. Most content providers store their da ta using Android's file storage methods or SQLite databases, but you can store your da ta any way you want. Android provides the SQLiteOpenHelper class to help you create a database and SQLiteDatabase to manage it.

l Extend the ContentProvider class to provide access to the da ta.

n 重写query()/insert()/update()/delete()/getType()/on Create()

l Declare the content provider in the manifest file for your application (AndroidManifest.xml).

需要注意的:

1) query()必须返回Cursor对象以让调用者遍历返回的数据。Cursor本身就是一个接口,不过Android系统提供了预定义的Cursor,必然SQLiteCursor可以遍历SQLite数据库。

2) 定义一个“public static final Uri CONTENT_URI”来表示能提供的数据,这个URI必须是唯一的,例如:public static final Uri CONTENT_URI = Uri.parse("content://com.example.codelab.transporationprovider");如果内容提供者有子表,需要对每一个子table定义一个CONTENT_URI,由不同路径来表示(即URI后跟不同的/xxx来表示不同的类型)。

3) 定义content provider返回给用户的列的名字。必须包含_ID

4) 如果提供的数据类型是全新的,那么ContentProvider.getType()就需要返回一个自定义的MIME类型。

5) 如果提供的数据由于太大而不能放在表里边(比如大位图或mp3文件),返回给用户的应该是指向此文件的URI;这个记录还需要另一个域,叫做“_da ta”,它指向设备商文件的确切路径。用户通过ContentResolver.openInputStream()来打开此文件。



声明Content provider

在AndroidMenifest.xml中添加类似如下的声明:



<provider name="com.example.autos.AutoInfoProvider"

authorities="com.example.autos.autoinfoprovider" 

. . . />

</provider>

你可能感兴趣的:(5.4、Android中数据存储和访问-使用ContentProvider共享数据)