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的工具类,分别为UriMatcher和ContentUris。掌握它们的使用,会便于我们的开发工作。
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 | 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路径,返回的匹配码为1
ContentUris类使用介绍
ContentUris类用于操作Uri路径后面的ID部分,它有两个比较实用的方法:
static long | 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.personprovider的ContentProvider进行操作,操作的数据为person表中id为10的记录。
4.创建ContentProvider
要创建我们自己的Content Provider的话,我们需要遵循以下几步:
a. 创建一个继承了ContentProvider父类的类
b. 定义一个名为CONTENT_URI,并且是public static final的Uri类型的类变量,你必须为其指定一个唯一的字符串值,最好的方案是以类的全名称,如:
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类型)
比如, 一个请求列车信息的URI如content://com.example.transportationprovider/trains/122可能就会返回typevnd.android.cursor.item/vnd.example.rail这样一个MIME类型。
vnd.android.cursor.dir/vnd.yourcompanyname.contenttype(多个记录的MIME类型)
比如, 一个请求所有列车信息的URI如content://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);
}
}