浅析——ContentProvider的使用

来源:互联网 发布:天生丽质难自弃网络语 编辑:程序博客网 时间:2024/06/05 19:54

ContentProvider

1.ContentProvider简介:

ContentProvider是Android四大组件之一,是不同应用程序之间进行数据交换的标准API,ContentProvider以某种Uri的形式对外提供数据,允许其他应用访问或修改数据;其它应用程序使用ContentResolver根据Uri访问操作指定数据。

2.Uri简介

Uri代表了要操作的数据,Uri主要包含了两部分信息:1.需要操作的ContentProvider,2.对ContentProvider中的什么数据进行操作,一个Uri由以下几部分组成:

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

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

3.路径(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")

3.UriMatcher、ContentUrist和ContentResolver简介

因为Uri代表了要操作的数据,所以我们经常需要解析Uri,并从Uri中获取数据。Android系统提供了两个用于操作Uri的工具类,分别为UriMatcherContentUris。掌握它们的使用,会便于我们的开发工作。

UriMatcher有两个方法:

Public Methods

 

void

addURI(String authority, String path, int code)

Add a URI to match, and the code to return when this URI is matched.

int

match(Uri uri)

Try to match against the path in a url.


UriMatcher类用于匹配Uri,它的用法如下:
首先第一步把你需要匹配Uri路径全部给注册上,如下:

//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配content://com.ljq.provider.personprovider/person路径,返回匹配码为1
sMatcher.addURI("com.ljq.provider.personprovider","person", 1);//添加需要匹配uri,如果匹配就会返回匹配码
//如果match()方法匹配content://com.ljq.provider.personprovider/person/230路径,返回匹配码为2
sMatcher.addURI("com.ljq.provider.personprovider","person/#", 2);//#号为通配符
switch(sMatcher.match(Uri.parse("content://com.ljq.provider.personprovider/person/10"))){
   case 1
     break;
   case 2
     break;
   default://不匹配
    break;
}

注册完需要匹配的Uri后,就可以使用sMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用addURI()方法传入的第三个参数,假设匹配content://com.ljq.provider.personprovider/person路径,返回的匹配码为

ContentUris类使用介绍

ContentUris类用于操作Uri路径后面的ID部分,它有两个比较实用的方法:

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.

withAppendedId(uri,id)用于为路径加上ID部分:

Uri uri =Uri.parse("content://com.ljq.provider.personprovider/person")
Uri resultUri = ContentUris.withAppendedId(uri, 10);
//生成后的Uri为:content://com.ljq.provider.personprovider/person/10

parseId(uri)方法用于从路径中获取ID部分:

Uri uri =Uri.parse("content://com.ljq.provider.personprovider/person/10")
long personid = ContentUris.parseId(uri);//获取的结果为:10

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和对其中的什么数据进行操作,

假设给定的是:Uri.parse("content://com.ljq.providers.personprovider/person/10"),那么将会对主机名为com.ljq.providers.personproviderContentProvider进行操作,操作的数据为person表中id10的记录。

4.创建ContentProvider

要创建我们自己的Content Provider的话,我们需要遵循以下几步:
a.
创建一个继承了ContentProvider父类的类

b.
定义一个名为CONTENT_URI,并且是public static finalUri类型的类变量,你必须为其指定一个唯一的字符串值,最好的方案是以类的全名称,:
public static final Uri CONTENT_URI = Uri.parse(“content://com.google.android.MyContentProvider”);

c. 定义你要返回给客户端的数据列名。如果你正在使用Android数据库,必须为其定义一个叫_id的列,它用来表示每条记录的唯一性。

d. 创建你的数据存储系统。大多数Content Provider使用Android文件系统或SQLite数据库来保持数据,但是你也可以以任何你想要的方式来存储。

e.
如果你要存储字节型数据,比如位图文件等,数据列其实是一个表示实际保存文件的URI字符串,通过它来读取对应的文件数据。处理这种数据类型的Content Provider需要实现一个名为_data的字段,_data字段列出了该文件在Android文件系统上的精确路径。这个字段不仅是供客户端使用,而且也可以供ContentResolver使用。客户端可以调用ContentResolver.openOutputStream()方法来处理该URI指向的文件资源;如果是ContentResolver本身的话,由于其持有的权限比客户端要高,所以它能直接访问该数据文件。

f.
声明public staticString型的变量,用于指定要从游标处返回的数据列。

g.
查询返回一个Cursor类型的对象。所有执行写操作的方法如insert(), update()以及delete()都将被监听。我们可以通过使用ContentResover().notifyChange()方法来通知监听器关于数据更新的信息。

h.
AndroidMenifest.xml中使用<provider>标签来设置Content Provider

i.
如果你要处理的数据类型是一种比较新的类型,你就必须先定义一个新的MIME类型,以供ContentProvider.geType(url)来返回。MIME类型有两种形式:一种是为指定的单个记录的,还有一种是为多条记录的。这里给出一种常用的格式:

vnd.android.cursor.item/vnd.yourcompanyname.contenttype(单个记录的MIME类型)
  比如, 一个请求列车信息的URIcontent://com.example.transportationprovider/trains/122可能就会返回typevnd.android.cursor.item/vnd.example.rail这样一个MIME类型。

vnd.android.cursor.dir/vnd.yourcompanyname.contenttype(多个记录的MIME类型)
  比如, 一个请求所有列车信息的URIcontent://com.example.transportationprovider/trains可能就会返回vnd.android.cursor.dir/vnd.example.rail这样一个MIME类型。

5.ContentProvider的应用实例

运行效果:

 1.常量类,声明需要使用的常量(Dicts类)

package com.jph.dictdemo;import android.net.Uri;import android.provider.BaseColumns;/**  *Describe:</br> *常量类,声明要使用的常量</br> *内部类Dict实现了BaseColumns接口,该接口也是一个常量接口</br> *该常量接口中已经定义了_ID、WORD、DETAIL常量,分别用来表示记录的id、单词、单词解释</br> *@author jph</br> *Date:2014.07.13 */public class Dicts {//定义ContentProvider的Authoritypublic static final String AUTHORITY="com.jph.dictdemo.dictprovider";//定义一个静态内部类,定义该ContentProvider所包含数据列的列明public static final class Dict implements BaseColumns{//定义Content所允许操作的三个数据列名public static final String _ID="_id";public static final String WORD="word"; //单词public static final String DETAIL="detail";//单词解释public static final Uri DICT_CONTENT_URI=Uri.parse("content://"+AUTHORITY+"/words");public static final Uri WORD_CONTENT_URI=Uri.parse("content://"+AUTHORITY+"/word");public static final String CONTENT_TYPE="vnd.android.cursor.dir/com.jph.dictdemo.dict";    public static final String CONTENT_ITEM_TYPE="vnd.android.cursor.item/com.jph.dictdemo.dict";    public static final String DB_NAME="dict.db3";//数据库名public static final String T_NAME="tb_dict";//表名       }}

 2.利用数据库帮助类SQLiteOpenHelper来创建数据库(DbHelper类)

package com.jph.dictdemo;import android.content.Context;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;/** * Describe:</br> * 在创建表的时候除了主键外其它的字段没有指定数据类型,因为SQLiteQLite </br> * 在解析CREATE TABLE 语句时,会忽略 CREATE TABLE 语句中跟在字段名后面的数据类型信息 </br> * @author jph</br> * Date:2014.07.13 * */public class DbHelper extends SQLiteOpenHelper {final String CREATE_TABLE_SQL="create table tb_dict("+Dicts.Dict._ID+" integer primary key autoincrement,"+Dicts.Dict.WORD+","+Dicts.Dict.DETAIL+")";public DbHelper(Context context, String name,int version) {super(context, name, null, version);// TODO Auto-generated constructor stub}@Overridepublic void onCreate(SQLiteDatabase db) {// TODO Auto-generated method stub//第一次使用数据库是自动创建表db.execSQL(CREATE_TABLE_SQL);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {// TODO Auto-generated method stubSystem.out.println("--------------onUpgrade Called----------------"+"oldVersion:"+oldVersion+"  newVersion"+newVersion);}}

3.创建DictProvider类继承ContentProvider

package com.jph.dictdemo;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;/** * Describe:</br> * Dict程序的为了向外界提供数据,创建了继承ContentProvider的子类</br> * DictProvider并实现其所有的抽象方法,如:insert、delete、query、</br> * delete、update等方法。</br> * @author jph</br> * Date:2014.07.13 * */public class DictProvider extends ContentProvider {//定义数据库帮助类对象private DbHelper dbOpenHelper;private static final int WORDS=1;private static final int WORD=2;private static UriMatcher matcher=new UriMatcher(UriMatcher.NO_MATCH);static {//添加需要匹配的Uri,匹配则返回相应的匹配码matcher.addURI(Dicts.AUTHORITY, "words", 1);matcher.addURI(Dicts.AUTHORITY, "word/#", 2);}public DictProvider() {// TODO Auto-generated constructor stub}@Overridepublic int delete(Uri uri, String selection, String[] selectionArgs) {// TODO Auto-generated method stubSQLiteDatabase db=dbOpenHelper.getWritableDatabase();//纪录所删除的纪录数int count=0;switch (matcher.match(uri)) {// 如果Uri参数代表操作全部数据项case WORDS:count=db.delete(Dicts.Dict.T_NAME, selection, selectionArgs);break;// 如果Uri参数代表指定数据项case WORD://解析出所要删除单词的IDlong id=ContentUris.parseId(uri);String whereClause=Dicts.Dict._ID+"="+id;//如果原来的where子句存在,拼接where子句if (selection!="") {whereClause=whereClause+"and"+selection;}count=db.delete(Dicts.Dict.T_NAME, whereClause, selectionArgs);break;default:throw new IllegalArgumentException("未知Uri:"+uri);}//通知数据已经改变getContext().getContentResolver().notifyChange(uri, null);return count;}@Overridepublic String getType(Uri uri) {// TODO Auto-generated method stubswitch (matcher.match(uri)) {//如果操作的是多项纪录case WORDS:return Dicts.Dict.CONTENT_TYPE;// 如果操作的数据是单项记录case WORD:return Dicts.Dict.CONTENT_ITEM_TYPE;default:throw new IllegalArgumentException("未知Uri:" + uri);}}@Overridepublic Uri insert(Uri uri, ContentValues values) {// TODO Auto-generated method stubSQLiteDatabase db=dbOpenHelper.getReadableDatabase();switch (matcher.match(uri)) {// 如果Uri参数代表操作全部数据项case WORDS:long rowId=db.insert(Dicts.Dict.T_NAME, Dicts.Dict._ID, values);//如果插入成功则返回Uriif (rowId>0) {//在已有的Uri后面追加IDUri urinew=ContentUris.withAppendedId(uri, rowId);//通知数据已经改变getContext().getContentResolver().notifyChange(uri, null);return urinew;}break;default:throw new IllegalArgumentException("未知Uri:"+uri);}return null;}//初始化@Overridepublic boolean onCreate() {// TODO Auto-generated method stub//创建db对象dbOpenHelper=new DbHelper(this.getContext(),Dicts.Dict.DB_NAME, 1);return true;}@Overridepublic Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {// TODO Auto-generated method stubSQLiteDatabase db=dbOpenHelper.getReadableDatabase();SQLiteDatabase db=dbOpenHelper.getReadableDatabase();switch (matcher.match(uri)) {// 如果Uri参数代表操作全部数据项case WORDS:return db.query(Dicts.Dict.T_NAME, projection, selection, selectionArgs, null, null, null);// 如果Uri参数代表指定数据项case WORD://解析出所要查询单词的IDlong id=ContentUris.parseId(uri);String whereClause=Dicts.Dict._ID+"="+id;//如果原来的where子句存在,拼接where子句if (selection!="") {whereClause=whereClause+"and"+selection;}return db.query(Dicts.Dict.T_NAME, projection, whereClause, selectionArgs, null, null, null);default:throw new IllegalArgumentException("未知Uri:"+uri);}}@Overridepublic int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) {// TODO Auto-generated method stubSQLiteDatabaseSQLiteDatabase db=dbOpenHelper.getReadableDatabase();//纪录所更新的纪录数int count=0;switch (matcher.match(uri)) {// 如果Uri参数代表操作全部数据项case WORDS:count=db.update(Dicts.Dict.T_NAME, values, selection, selectionArgs);break;// 如果Uri参数代表指定数据项case WORD://解析出所要更新单词的IDlong id=ContentUris.parseId(uri);String whereClause=Dicts.Dict._ID+"="+id;//如果原来的where子句存在,拼接where子句if (selection!="") {whereClause=whereClause+"and"+selection;}count=db.update(Dicts.Dict.T_NAME, values, whereClause, selectionArgs);break;default:throw new IllegalArgumentException("未知Uri:"+uri);}//通知数据已经改变getContext().getContentResolver().notifyChange(uri, null);return count;}}

4.创建一个应用来测试,查询、删减、插入、更新数据(DictResolver)

该应用主要有两个Activity,一个用于操作ContentProvider,一个用于显示操作结果

DictResolver类:

package com.jph.dictresolver;import java.util.ArrayList;import java.util.HashMap;import java.util.Map;import android.os.Bundle;import android.app.Activity;import android.content.ContentResolver;import android.content.ContentValues;import android.content.Intent;import android.database.Cursor;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.EditText;/** * Describe:</br> * 本实例通过获取系统的ContentResolver对象,对DictDemo实例的</br> * ContentProvider中的数据进行GRUD操作</br> * @author jph * Date:<2014.07.13> * */public class DictResolver extends Activity {ContentResolver resolver;Button btnInsert,btnSelect;EditText edtWord,edtDetail,edtSelect;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);//获取系统的ContentResolver对象resolver=getContentResolver();edtDetail=(EditText)findViewById(R.id.edtDetail);edtWord=(EditText)findViewById(R.id.edtWord);edtDetail=(EditText)findViewById(R.id.edtDetail);edtSelect=(EditText)findViewById(R.id.edtSelect);btnInsert=(Button)findViewById(R.id.btnInsert);btnSelect=(Button)findViewById(R.id.btnSelect);btnInsert.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubString detail=edtDetail.getText().toString();String word=edtWord.getText().toString();//定义ContentValues用于存储单词纪录的名值对ContentValues values=new ContentValues();values.put(Dicts.Dict.WORD, word);values.put(Dicts.Dict.DETAIL, detail);resolver.insert(Dicts.Dict.DICT_CONTENT_URI, values);edtWord.setText("");edtDetail.setText("");}});btnSelect.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubString key=edtSelect.getText().toString();//执行查询Cursor cursor=resolver.query(Dicts.Dict.DICT_CONTENT_URI, null, "word like ? or detail like ?", new String[]{"%"+key+"%","%"+key+"%"}, null);//创建一个Bundle对象Bundle bundle=new Bundle();bundle.putSerializable("list", convertCursorToList(cursor));Intent intent=new Intent(DictResolver.this,ResultShow.class);intent.putExtras(bundle);startActivity(intent);}});}//将Cursor类型的查询结果转换成序列化的Listprotected ArrayList<Map<String, String>> convertCursorToList(Cursor cursor) {// TODO Auto-generated method stubArrayList<Map<String, String>>list=new  ArrayList<Map<String,String>>();//表里cursor结果集while (cursor.moveToNext()) {//将结果集中的数据放入ArrayList中Map<String, String> listItem=new HashMap<String, String>();listItem.put("word",cursor.getString(1));listItem.put("detail", cursor.getString(2));list.add(listItem);}return list;}}

ResultShow类:

package com.jph.dictresolver;import java.util.List;import java.util.Map;import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.widget.ListView;import android.widget.SimpleAdapter;public class ResultShow extends Activity {ListView listView;@Overrideprotected void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);setContentView(R.layout.result);listView=(ListView)findViewById(R.id.list);Intent intent=getIntent();//获取Intent所携带的数据Bundle bundle=intent.getExtras();//从Bundle包中取出数据@SuppressWarnings("unchecked")List<Map<String, String>>list=(List<Map<String, String>>) bundle.getSerializable("list");//将List封装成AdapterSimpleAdapter adapter=new SimpleAdapter(this, list, R.layout.line, new String[]{"word","detail"}, new int[]{R.id.txtWord,R.id.txtDetail});//显示数据listView.setAdapter(adapter);}}





 

10 0
原创粉丝点击