ContentProvider 在android中的作用是对外共享数据,也就是说你可以通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider 对你应用中的数据进行添删改查。关于数据共享,以前我们学习过文件操作模式,知道通过指定文件的操作模式为Context.MODE_WORLD_READABLE 或Context.MODE_WORLD_WRITEABLE同样也可以对外共享数据。那么,这里为何要使用ContentProvider 对外共享数据呢?是这样的,如果采用文件操作模式对外共享数据,数据的访问方式会因数据存储的方式而不同,导致数据的访问方式无法统一,如:采用xml文件对外共享数据,需要进行xml解析才能读取数据;采用sharedpreferences共享数据,需要使用sharedpreferences API读取数据。
使用ContentProvider对外共享数据的好处是统一了数据的访问方式。
ContentProvider的原理是按照一定规则暴露自己的接口给其它应用来访问自己应用的数据(其实就是自定义增删改查接口并暴露出去,让别的应用访问自己的数据)。
ContentResolver就是按照一定规则访问内容提供者的数据(其实就是调用内容提供者自定义的接口来操作它的数据)。
采用了authorities(主机名/域名)对它进行唯一标识,你可以把 ContentProvider看作是一个网站(想想,网站也是提供数据者),authorities 就是他的域名:
例如:要得到所有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”。
掌握它们的使用,会便于我们的开发工作。
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
package com.itheima.transaction;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
public class AccountProvider extends ContentProvider {
private static final int QUEYSUCESS = 0; // ctrl + shift + X(变大写) 变小写 + y
private static final int INSERTSUCESS = 1;
private static final int UPDATESUCESS = 2;
private static final int DELSUCESS = 3;
//1 想使用内容提供者 必须定义 匹配规则 code:定义的匹配规则 如果 匹配不上 有一个返回码 -1
static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
private MyOpenHelper helper;
//2 我要添加匹配规则
static{
//开始添加匹配规则
/**
* authority 主机名 通过主机名来访问我暴露的数据
* path 你也可以随意 写 com.itheima.contentprovider/query
* code 匹配码
*/
matcher.addURI("com.itheima.contentprovider", "query", QUEYSUCESS);
//添加插入匹配规则
matcher.addURI("com.itheima.contentprovider", "insert", INSERTSUCESS);
//添加更新匹配规则
matcher.addURI("com.itheima.contentprovider", "update", UPDATESUCESS);
//添加删除匹配规则
matcher.addURI("com.itheima.contentprovider", "delete", DELSUCESS);
}
@Override
public boolean onCreate() {
helper = new MyOpenHelper(getContext());
return false;
}
//Uri 范围比较大 不但可以指定 tel: 可以定义很多语法
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
//传递过来的uri 是否和我们定义的匹配规则 匹配
int match = matcher.match(uri);
if (match == QUEYSUCESS ) {
//说明匹配成功
SQLiteDatabase db = helper.getReadableDatabase(); //获取数据库对象
Cursor cursor = db.query("info", projection, selection, selectionArgs, null, null, sortOrder);
//注意 这个地方 不要关闭 cursor 和 db
//大吼一声 数据库发生了改变
getContext().getContentResolver().notifyChange(uri, null);
return cursor;
}else{
//匹配失败
throw new IllegalArgumentException("路径匹配失败");
}
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
int match = matcher.match(uri);
if (match == INSERTSUCESS) {
//说明匹配成功
SQLiteDatabase db = helper.getReadableDatabase();
long insert = db.insert("info", null, values);
//执行上面这句话 说明我的数据库内容 发生了变化 首先 要发送一条通知 说明 我发生改变
if (insert>0) {
//数据库发生变化 发送一个通知
getContext().getContentResolver().notifyChange(uri, null);
}
Uri uri2 = Uri.parse("com.itheima.contentprovider/"+insert);
return uri2;
}else{
//匹配失败
throw new IllegalArgumentException("路径匹配失败");
}
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int match = matcher.match(uri);
if (match == DELSUCESS) {
//匹配成功
SQLiteDatabase db = helper.getReadableDatabase();
int delete = db.delete("info", selection, selectionArgs);
if (delete>0) {
//大吼一声 数据库发生了改变
getContext().getContentResolver().notifyChange(uri, null);
}
return delete;
}else {
//匹配失败
throw new IllegalArgumentException("路径匹配失败");
}
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
int match = matcher.match(uri);
if (match == UPDATESUCESS) {
//匹配成功
SQLiteDatabase db = helper.getReadableDatabase();
int update = db.update("info", values, selection, selectionArgs);
if (update>0) {
//大吼一声 数据库发生了改变
getContext().getContentResolver().notifyChange(uri, null);
}
return update;
}else {
//匹配失败
throw new IllegalArgumentException("路径匹配失败");
}
}
}
可以使用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");
resolver.update(updateIdUri, updateValues, null, null);
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,//true表示只要发出通知的Uri以方法第一个参数开头都能被监听到,否侧监听uri必须与发出通知的uri完全匹配才能被监听到
new PersonObserver(new Handler()));
public class PersonObserver extends ContentObserver{
public PersonObserver(Handler handler) {
super(handler);
}
public void onChange(boolean selfChange) {
//此处可以进行相应的业务处理
}
}
package com.itheima.backupsms;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import org.xmlpull.v1.XmlSerializer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.FileObserver;
import android.app.Activity;
import android.database.Cursor;
import android.util.Xml;
import android.view.Menu;
import android.view.View;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/**
* 点击按钮备份短信
*
* @param v
*/
public void backup(View v) {
try {
// 1 初始化xml 序列化器
XmlSerializer serializer = Xml.newSerializer();
// 2初始化xml序列化器的 参数
File file = new File(Environment.getExternalStorageDirectory()
.getPath(), "smsbackup.xml");
FileOutputStream fos = new FileOutputStream(file);
serializer.setOutput(fos, "utf-8");
// 3 开始写 xml 头
serializer.startDocument("utf-8", true);
// 开始写 xml 根节点 smss
serializer.startTag(null, "smss");
// 1 把我们关系的短信数据库里的内容 给 获取出来 我们要做的操作 就是通过内容解析者 把数据给查询出来
Uri uri = Uri.parse("content://sms");
Cursor cursor = getContentResolver().query(uri,
new String[] { "address", "date", "body" }, null, null,
null);
while (cursor.moveToNext()) {
// 写sms 节点
serializer.startTag(null, "sms");
String address = cursor.getString(0);
String date = cursor.getString(1);
String body = cursor.getString(2);
//开始写 address 节点
serializer.startTag(null, "address");
serializer.text(address);
serializer.endTag(null, "address");
//开始写 date 节点
serializer.startTag(null, "date");
serializer.text(date);
serializer.endTag(null, "date");
//开始写 body 节点
serializer.startTag(null, "body");
serializer.text(body);
serializer.endTag(null, "body");
serializer.endTag(null, "sms");
}
// 开始写 address date body
serializer.endTag(null, "smss");
// 文档结束
serializer.endDocument();
} catch (Exception e) {
e.printStackTrace();
}
}
}
//1 先拿到 内容解析器
Uri uri = Uri.parse("content://sms");
ContentValues values = new ContentValues();
values.put("address", "110");
values.put("date", System.currentTimeMillis());
values.put("body", "您的事犯了 请您马上来一趟");
getContentResolver().insert(uri, values);
package com.itheima.getcontactinfo.utils;
import java.util.ArrayList;
import java.util.List;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import com.itheima.getcontactinfo.domain.ContactInfo;
public class ContactUtils {
public static List getContactInfos(Context context) {
List contactLists = new ArrayList();
// 1 首先 我要查询raw_contacts表 获取到 contact_id列
// (1)我如何查询 ?用 内容解析者 path
Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
Uri datauri = Uri.parse("content://com.android.contacts/data");
ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(uri, new String[] { "contact_id" },
null, null, null);
while (cursor.moveToNext()) {
// 获取到contact_id 的值
String contact_id = cursor.getString(0);
System.out.println("contact_id--" + contact_id);
// 有个小细节注意一下 我要判断 contact_id 是否为空
if (contact_id != null) {
ContactInfo info = new ContactInfo();
info.setId(contact_id);
// 2去data表 根据 contact_id 去获取 mimetyple_id列 data1 列
Cursor dataCursor = resolver.query(datauri, new String[] {
"mimetype", "data1" }, "raw_contact_id=?",
new String[] { contact_id }, null);
while (dataCursor.moveToNext()) {
String mimetype = dataCursor.getString(0); // 获取到mimeytype
String data1 = dataCursor.getString(1);
// 3 然后根据 mimetype类型 来区分数据类型
if ("vnd.android.cursor.item/email_v2".equals(mimetype)) {
info.setEmail(data1);
} else if ("vnd.android.cursor.item/name".equals(mimetype)) {
info.setName(data1);
} else if ("vnd.android.cursor.item/phone_v2"
.equals(mimetype)) {
System.out.println("data---电话号码-" + data1);
info.setPhone(data1);
}
}
dataCursor.close();
contactLists.add(info); // 把info信息存到集合中
}
}
cursor.close();
return contactLists;
}
}
package com.itheima.getcontactinfo.domain;
public class ContactInfo {
private String id;
private String name;
private String phone;
private String email;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "ContactInfo [id=" + id + ", name=" + name + ", phone=" + phone
+ ", email=" + email + "]";
}
}
package com.itheima.insert.contact;
import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
public class MainActivity extends Activity {
private EditText et_name;
private EditText et_phone;
private EditText et_email;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//1找到我们关心的控件
et_name = (EditText) findViewById(R.id.et_name);
et_phone = (EditText) findViewById(R.id.et_phone);
et_email = (EditText) findViewById(R.id.et_email);
}
/**
* 保存联系人的信息
* @param v
*/
public void click(View v){
//1 要先拿到 edittext的值
String name = et_name.getText().toString().trim();
String phone = et_phone.getText().toString().trim();
String email = et_email.getText().toString().trim();
//2 往 raw_contacts里插入一条数据 拿到内容解析者
Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
Uri datauri = Uri.parse("content://com.android.contacts/data");
ContentValues values = new ContentValues();
// 插入数据之前 我要先查询 一下 raw_contacts 表一共有多少行的数据
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
int count = cursor.getCount(); //获取到一共有多少行
int contact_id = count + 1;
values.put("contact_id", contact_id);
getContentResolver().insert(uri, values);
// 3 往data 表插入数据
ContentValues nameValues = new ContentValues();
nameValues.put("data1", name);
nameValues.put("mimetype", "vnd.android.cursor.item/name");
nameValues.put("raw_contact_id", contact_id);
getContentResolver().insert(datauri, nameValues);
ContentValues phoneValues = new ContentValues();
phoneValues.put("data1", phone);
phoneValues.put("mimetype", "vnd.android.cursor.item/phone_v2");
phoneValues.put("raw_contact_id", contact_id);
getContentResolver().insert(datauri, phoneValues);
ContentValues emailValues = new ContentValues();
emailValues.put("data1", email);
emailValues.put("mimetype", "vnd.android.cursor.item/email_v2");
emailValues.put("raw_contact_id", contact_id);
getContentResolver().insert(datauri, emailValues);
}
}
package com.itheima.smslistener;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.database.ContentObserver;
import android.database.Cursor;
import android.view.Menu;
public class MainActivity extends Activity {
private Uri uri;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//1注册一个内容观察者
uri = Uri.parse("content://sms");
getContentResolver().registerContentObserver(uri, true, new MyObserver(new Handler()));
}
private class MyObserver extends ContentObserver{
public MyObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
//当短信的数据库发生了变化 我就去取出所有短信的内容
Cursor cursor = getContentResolver().query(uri, new String[]{"address","body","date"}, null, null, null);
while(cursor.moveToNext()){
String address = cursor.getString(0);
String body = cursor.getString(1);
String date = cursor.getString(2);
System.out.println("address---"+address+"--body:"+body);
}
super.onChange(selfChange);
}
}
}