浅析——ContentProvider的使用

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的Authority
	public 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
	}
	@Override
	public void onCreate(SQLiteDatabase db) {
		// TODO Auto-generated method stub
		//第一次使用数据库是自动创建表
		db.execSQL(CREATE_TABLE_SQL);
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		// TODO Auto-generated method stub
		System.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
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		// TODO Auto-generated method stub
		SQLiteDatabase 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:
			//解析出所要删除单词的ID
			long 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;
	}

	@Override
	public String getType(Uri uri) {
		// TODO Auto-generated method stub
		switch (matcher.match(uri)) {
		//如果操作的是多项纪录
		case WORDS:
			return Dicts.Dict.CONTENT_TYPE;
		// 如果操作的数据是单项记录
		case WORD:
			return Dicts.Dict.CONTENT_ITEM_TYPE;		
		default:
			throw new IllegalArgumentException("未知Uri:" + uri);
		}
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		// TODO Auto-generated method stub
		SQLiteDatabase db=dbOpenHelper.getReadableDatabase();
		switch (matcher.match(uri)) {
		// 如果Uri参数代表操作全部数据项
		case WORDS:
			long rowId=db.insert(Dicts.Dict.T_NAME, Dicts.Dict._ID, values);
			//如果插入成功则返回Uri
			if (rowId>0) {
				//在已有的Uri后面追加ID
				Uri urinew=ContentUris.withAppendedId(uri, rowId);
				//通知数据已经改变
				getContext().getContentResolver().notifyChange(uri, null);
				return urinew;
			}
			break;		
		default:
			throw new IllegalArgumentException("未知Uri:"+uri);
		}
		return null;
		
	}
	//初始化
	@Override
	public boolean onCreate() {
		// TODO Auto-generated method stub
		//创建db对象
		dbOpenHelper=new DbHelper(this.getContext(),Dicts.Dict.DB_NAME, 1);		
		return true;
	}

	@Override
	public 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:
			//解析出所要查询单词的ID
			long 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);
		}
		
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		// TODO Auto-generated method stubSQLiteDatabase
		SQLiteDatabase 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:
			//解析出所要更新单词的ID
			long 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;
	@Override
	protected 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() {			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				String 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() {			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				String 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类型的查询结果转换成序列化的List
	protected ArrayList<Map<String, String>> convertCursorToList(Cursor cursor) {
		// TODO Auto-generated method stub
		ArrayList<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;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.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封装成Adapter
		SimpleAdapter adapter=new SimpleAdapter(this, list, R.layout.line, 
				new String[]{"word","detail"}, new int[]{R.id.txtWord,R.id.txtDetail});
		//显示数据
		listView.setAdapter(adapter);
		
		
	}

	

}





 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值