Android 之内容提供者 内容解析者 内容观察者
contentProvider:ContentProvider在Android中的作用是对外提供数据,除了可以为所在应用提供数据外,还可以共享数据给其他应用,这是Android中解决应用之间数据共享的机制。
通过ContentProvider我们可以对数据进行增删改查的操作。使用ContentProvider对外共享数据的好处是统一了数据的访问方式。
ContentProvider在android中的作用是对外共享数据,也就是说你可以通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider对你应用中的数据进行添删改查。
关于数据共享,以前我们学习过文件操作模式,知道通过指定文件的操作模式为Context.MODE_WORLD_READABLE或Context.MODE_WORLD_WRITEABLE同样也可以对外共享数据。
那么,这里为何要使用ContentProvider对外共享数据呢?
是这样的,如果采用文件操作模式对外共享数据,数据的访问方式会因数据存储的方式而不同,导致数据的访问方式无法统一,如:
采用xml文件对外共享数据,需要进行xml解析才能读取数据;采用sharedpreferences共享数据,需要使用sharedpreferences API读取数据。
一般的数据库比如sqlitedatabase,是不能跨应用访问的,比如A.apk中的数据不能被B.apk使用,ContentProvider则可以跨平台访问数据。
默认的内容提供者方式应用有音视频、图片、通讯录、日历等。
示例:
第一步.首先在清单文件中添加单元测试授权和包
第二步.ContentProvider对外提供了完整的增删查改的操作,对内是通过数据库的操作实现的。
首先创建一个DBHelper类,让它继承SQLiteOpenHelper
package com.example.android_07contentprovider; import android.content.Context;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper; public class DBHelper extends SQLiteOpenHelper { private static String name="mydb.db";
private static int version=;
public DBHelper(Context context) {
super(context, name, null, version);
// TODO Auto-generated constructor stub
}
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
String sql="create table student(id integer primary key autoincrement,name varchar(64),address varchar(64))";
db.execSQL(sql);//对表的创建
} @Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub } }
第三步.定义一个内容提供者studentProvider 继承ContentProvider。
并在清单文件中进行注册,在注册ContentProvider时,采用了authorities(主机名/域名)的方法对它进行唯一标识,
ContentProvider的标识类似于网站的域名,通过此域名我们可以准确访问到对应网站,网站为我们提供数据。
Authorities就是ContentProvider的域名。注册方法如下:
<provider android:name=".studentProvider" android:authorities="com.example.android_07contentprovider.studentProvider"></provider>
0.在create方法中实例化一个DBhelper对象:helper=new DBHelper(getContext());
1.在内容提供者内部声明一个UriMatcher对象,用来与内容解析者发送的地址进行匹配判断
2.声明标识位,用来判断操作单条还是多条记录
3.声明静态代码模块,用来定义匹配规则 *用来匹配所有的文字,#用来匹配所有的数字
matcher.addURI("授权地址", "一般设置成表名", 标志位);
static{
/**静态代码块定义匹配规则
* 清单文件中注册的授权地址
* 路径 通常设置为表名
*/
matcher.addURI("com.example.android_07contentprovider.studentProvider", "student/#", STUDENT);
matcher.addURI("com.example.android_07contentprovider.studentProvider", "student", STUDENTS);
}
4.重写getType()方法,处理头部类型,来判断操作单条还是多条记录
public String getType(Uri uri) {
// TODO Auto-generated method stub
int flag=matcher.match(uri);
switch (flag) {
case STUDENT:
return "vnd.android.cursor.item/student";
case STUDENTS:
return "vnd.android.cursor.dir/students";
}
return null;
}
5.重写插入方法,返回uri供其他应用使用
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
Uri resUri=null;
int flag=matcher.match(uri);//uri参数是外部传递过来的规则 与提供者的规则进行匹配
switch (flag) {
case STUDENTS:
SQLiteDatabase db=helper.getWritableDatabase();
long id=db.insert("student", null, values);//返回id为插入当前行的行号
resUri=ContentUris.withAppendedId(uri, id);
break;
}
Log.i(TAG, "---->>"+resUri.toString());
return resUri;//content//返回给其他应用使用
}
6.测试:
a.需要定义一个内容解析者:ContentResolver resolver=getContext().getContentResolver();
b.调用内容解析者的insert方法:resolver.insert(uri, values);
c得到uri 和contentValues
最终源码:
项目结构:
MainActivity无变化
DBHelper工具类:
package com.example.android_07contentprovider; import android.content.Context;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper; public class DBHelper extends SQLiteOpenHelper { private static String name="mydb.db";
private static int version=;
public DBHelper(Context context) {
super(context, name, null, version);
// TODO Auto-generated constructor stub
}
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
String sql="create table student(id integer primary key autoincrement,name varchar(64),address varchar(64))";
db.execSQL(sql);//对表的创建
} @Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub } }
studentProvider:
package com.example.android_07contentprovider; 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;
import android.util.Log; public class studentProvider extends ContentProvider { private static final String TAG="studentProvider"; private DBHelper helper=null; private static final UriMatcher matcher=new UriMatcher(UriMatcher.NO_MATCH);
//标志位 分别表明操作单条、多条记录
private static final int STUDENT=;
private static final int STUDENTS=;
static{
/**静态代码块定义匹配规则
* 清单文件中注册的授权地址
* 路径 通常设置为表名
*/
matcher.addURI("com.example.android_07contentprovider.studentProvider", "student/#", STUDENT);
matcher.addURI("com.example.android_07contentprovider.studentProvider", "student", STUDENTS);
}
public studentProvider() {
// TODO Auto-generated constructor stub
} @Override
public boolean onCreate() {
// TODO Auto-generated method stub
helper=new DBHelper(getContext());
return false;
} @Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO Auto-generated method stub
Cursor cursor=null;
try {
int flag=matcher.match(uri);//uri参数是外部传递过来的规则 与提供者的规则进行匹配
SQLiteDatabase db=helper.getReadableDatabase();
switch (flag) {
case STUDENT:
//delete from student where id=? id是通过客户端传递过来的 需要进行截取
long id=ContentUris.parseId(uri);
String where_value="id="+id;
if(selection!=null&&!selection.equals("")){
where_value+="and"+selection;
}
cursor=db.query("student", null, where_value, selectionArgs, null, null, null, null);
break;
case STUDENTS:
cursor=db.query("student", null, null, selectionArgs, null, null, null, null);
break;
} } catch (Exception e) {
// TODO: handle exception
}
return cursor;
} /**
*
*/
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
int flag=matcher.match(uri);
switch (flag) {
case STUDENT:
return "vnd.android.cursor.item/student";
case STUDENTS:
return "vnd.android.cursor.dir/students";
}
return null;
} @Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
Uri resUri=null;
int flag=matcher.match(uri);//uri参数是外部传递过来的规则 与提供者的规则进行匹配
switch (flag) {
case STUDENTS:
SQLiteDatabase db=helper.getWritableDatabase();
long id=db.insert("student", null, values);//返回id为插入当前行的行号
resUri=ContentUris.withAppendedId(uri, id);
break;
}
Log.i(TAG, "---->>"+resUri.toString());
return resUri;//content//返回给其他应用使用
} @Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
int count=-;//表示影响数据库的行数
try {
int flag=matcher.match(uri);//uri参数是外部传递过来的规则 与提供者的规则进行匹配
SQLiteDatabase db=helper.getWritableDatabase();
switch (flag) {
case STUDENT:
//delete from student where id=? id是通过客户端传递过来的 需要进行截取
long id=ContentUris.parseId(uri);
String where_value="id="+id;
if(selection!=null&&!selection.equals("")){
where_value+="and"+selection;
}
count=db.delete("student", where_value, selectionArgs);
break;
case STUDENTS:
count=db.delete("student", selection, selectionArgs);
break;
} } catch (Exception e) {
// TODO: handle exception
}
return count;
} @Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO Auto-generated method stub
int count=-;//表示影响数据库的行数
try {
//update student set name=?,address=? where id=?
int flag=matcher.match(uri);//uri参数是外部传递过来的规则 与提供者的规则进行匹配
SQLiteDatabase db=helper.getWritableDatabase();
switch (flag) {
case STUDENT:
//delete from student where id=? id是通过客户端传递过来的 需要进行截取
long id=ContentUris.parseId(uri);
String where_value="id="+id;
if(selection!=null&&!selection.equals("")){
where_value+="and"+selection;
}
count=db.update("student", values,where_value, selectionArgs);
break;
} } catch (Exception e) {
// TODO: handle exception
}
return count;
} }
测试类(内容解析者)
package com.example.android_07contentprovider; import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.test.AndroidTestCase; public class MyTest extends AndroidTestCase { public MyTest() {
// TODO Auto-generated constructor stub
}
public void insert(){
//访问内容提供者的步骤
//1.需要一个内容解析者
ContentResolver resolver=getContext().getContentResolver();
//2.content://+授权 +表名来访问内容提供者
Uri uri=Uri.parse("content://com.example.android_07contentprovider.studentProvider/student");
ContentValues values=new ContentValues();
values.put("name", "zhangsan");
values.put("address", "zhogngguo");
resolver.insert(uri, values);
}
public void delete(){
ContentResolver resolver=getContext().getContentResolver();
//单条删除
/*Uri uri=Uri.parse("content://com.example.android_07contentprovider.studentProvider/student/1");
resolver.delete(uri, null,null);*/
//多条删除
Uri uri=Uri.parse("content://com.example.android_07contentprovider.studentProvider/student");
resolver.delete(uri, null,null);
}
public void modify(){
ContentResolver resolver=getContext().getContentResolver();
Uri uri=Uri.parse("content://com.example.android_07contentprovider.studentProvider/student/4");
ContentValues values=new ContentValues();
values.put("name", "zhaohaohao");
values.put("address", "jiaozuo");
resolver.update(uri, values, null, null);
}
public void query(){
ContentResolver resolver=getContext().getContentResolver();
//查询单条记录
Uri uri=Uri.parse("content://com.example.android_07contentprovider.studentProvider/student/5");
Cursor cursor=resolver.query(uri, null, null, null, null);
while(cursor.moveToNext()){
System.out.println(cursor.getString(cursor.getColumnIndex("name")));
}
//查询多条记录
/*Uri uri=Uri.parse("content://com.example.android_07contentprovider.studentProvider/student");
Cursor cursor=resolver.query(uri, null, null, null, null);
while(cursor.moveToNext()){
System.out.println(cursor.getString(cursor.getColumnIndex("name")));
}*/
}
}
--------------------------------------------------
内容观察者ContentObserver类详解:
观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理,它类似于 数据库技术中的触发器(Trigger),当ContentObserver所观察的Uri发生变化时,便会触发它。
1.在oncreate方法中注册一个观察者
// 注册内容观察者
getContentResolver().registerContentObserver(airplaneUri, false, airplaneCO);
抽象类ContentResolver类中的方法原型如下:
public final void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)
功能:为指定的Uri注册一个ContentObserver派生类实例,当给定的Uri发生改变时,回调该实例对象去处理。
参数:uri 需要观察的Uri(需要在UriMatcher里注册,否则该Uri也没有意义了)
notifyForDescendents 为false 表示精确匹配,即只匹配该Uri
为true 表示可以同时匹配其派生的Uri,举例如下:
假设UriMatcher 里注册的Uri共有一下类型:
1 、content://com.qin.cb/student
2 、content://com.qin.cb/student/#
3、 content://com.qin.cb/student/schoolchild(派生的Uri)
假设我们当前需要观察的Uri为content://com.qin.cb/student,如果发生数据变化的 Uri 为 content://com.qin.cb/student/schoolchild ,当notifyForDescendents为 false,那么该ContentObserver会监听不到,
但是当notifyForDescendents 为ture,能捕捉该Uri的数据库变化。
2.实现ContentObserver observer
//用来观察系统里短消息的数据库变化 ”表“内容观察者,只要信息数据库发生变化,都会触发该ContentObserver 派生类
public class SMSContentObserver extends ContentObserver {
private static String TAG = "SMSContentObserver"; private Context Context ; public SMSContentObserver(Context context,Handler handler) {
this.context = context ; }
/**
* 当所监听的Uri发生改变时,就会回调此方法
*
* @param selfChange 此值意义不大 一般情况下该回调值false
*/
@Override
public void onChange(boolean selfChange){
Log.i(TAG, "the sms table has changed"); //查询发件箱里的内容
Uri outSMSUri = Uri.parse("content://sms") ; Cursor c = Context.getContentResolver().query(uri, null, null, null,"date desc");
if(c != null){ Log.i(TAG, "the number of send is"+c.getCount()) ; StringBuilder sb = new StringBuilder() ;
//循环遍历
while(c.moveToNext()){
// sb.append("发件人手机号码: "+c.getInt(c.getColumnIndex("address")))
// .append("信息内容: "+c.getInt(c.getColumnIndex("body")))
// .append("是否查看: "+c.getInt(c.getColumnIndex("read")))
// .append("发送时间: "+c.getInt(c.getColumnIndex("date")))
// .append("\n");
sb.append("发件人手机号码: "+c.getInt(c.getColumnIndex("address")))
.append("信息内容: "+c.getString(c.getColumnIndex("body")))
.append("\n");
}
c.close();
sysout(sb.tostring()) ;
}
} }
构造方法 public void ContentObserver(Handler handler)
说明:所有 ContentObserver的派生类都需要调用该构造方法
参数: handler Handler对象。可以是主线程Handler(这时候可以更新UI 了),也可以是任何Handler对象。
重写方法
void onChange(boolean selfChange)
功能:当观察到的Uri发生变化时,回调该方法去处理。所有ContentObserver的派生类都需要重载该方法去处理逻辑。
参数:selfChange 回调后,其值一般为false,该参数意义不大。
以上讲清楚了如何让第三方观察公共内存消息邮箱的变化,那么系统短信应用 是如何发布出去自己的变化呢?
如果是我们自定义的ContentProvider数据源,而非系统的应用,例如短信应用,如果直接使用ContentObserver,那么数据源发生改变 后,就不能监听到任何反应。
实际上每个ContentProvider数据源发生改变后,如果想通知其监听对象ContentObserver时,必须在其对应方法 update / insert / delete时,
显示的调用context.getContentReslover().notifychange(Uri uri , ContentObserver observer)方法,回调监听处理逻辑。否则,我们的ContentObserver是不会监听到数据发生改变的。
参数:uri指的是数据源发生变化后,将变化放到哪一块内存空间中 可以是content://aaa.bbb.ccc
observer显示地指定一个观察者,指的是指定一个人来响应自己的操作,可以为null
接下来就可以自定义一个第三方应用,来接收刚才放到content://aaa.bbb.ccc中的内容了
步骤如上:1.在oncreate方法中注册一个观察者;2.实现ContentObserver observer
Android 之内容提供者 内容解析者 内容观察者的更多相关文章
- android之内容提供者解析
该系统有两个应用,比较繁琐.但是内容提供者是android里非常非常重要的一个内容,我们得好好学习下哦.先看效果图,我们提供了四个按钮,点击按钮便会进行相应的操作. 我们先看内容提供者所在的应用,代码 ...
- [Android Pro] 内容提供者ContentProvider的基本使用
一.ContentProvider简介 当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据.ContentProvider为存储和获取数据提 ...
- Android基础笔记(十四)- 内容提供者读取联系人
利用内容提供者读取联系人 利用内容提供者插入联系人 内容观察者的原理 利用内容观察者监听系统应用数据库或者自己应用数据库的变化 利用内容提供者读取联系人 读取联系人相对于读取短信来说就复杂非常多了,我 ...
- Android开发13——内容提供者ContentProvider的基本使用
一.ContentProvider简介 当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据.ContentProvider为存储和获取数据提 ...
- Android -- ContentProvider 内容提供者,创建和调用
1. 概述 ContentProvider 在android中的作用是对外共享数据,也就是说你可以通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentPr ...
- Android基础笔记(十三)- 内容提供者原理和简单使用
为什么要有内容提供者 内容提供者的工作原理 使用内容解析者对内容提供者进行增删改查操作 利用内容提供者和内容解析者备份手机短信 利用内容提供者插入短信 为什么要有内容提供者 内容提供者技术的目的是: ...
- 【Android】安卓四大组件之内容提供者
[Android]安卓四大组件之内容提供者 1.关于内容提供者 1.1 什么是内容提供者 内容提供者就是contentProvider,作用有如下: 给多个应用提供数据 类似一个接口 可以和多个应用分 ...
- Android学习---通过内容提供者(ContentProvider)操作另外一个应用私有数据库的内容
一.什么是ContentProvider? ContentProvider直译过来就是内容提供者,主要作用就是A应用提供接口给B应用调用数据,和之前介绍的sharedPreference和直接开放文件 ...
- Android组件系列----ContentProvider内容提供者
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
随机推荐
- VMware Workstation 12 Player之安装林耐斯-Linux Red Hat 7 -系统
Linux系统之Red Hat 7 安装笔记... Red Hat(红帽)公司(NYSE:RHT)是一家开源解决方案供应商,也是标准普尔500指数成员.总部位于美国北卡罗来纳州的罗利市,截止2015年 ...
- JavaScript练习题 全局变量 局部变量 作用域
前沿:大家好~我是阿飞~本次 任何简单的事情都可以复杂化,本次让我们来做下搞事情的练习题吧 例题1: var a = 1; function fn1(){ var a = 2; alert(a); / ...
- Andrew Ng机器学习课程笔记--week9(下)(推荐系统&协同过滤)
本周内容较多,故分为上下两篇文章. 本文为下篇. 一.内容概要 1. Anomaly Detection Density Estimation Problem Motivation Gaussian ...
- Maven详解(七)------ 创建Web工程以及插件原理
1.什么是 Maven 插件? 上一篇博客我们将了 Maven 的生命周期,我们知道 Maven 的核心是生命周期,生命周期指定了 Maven 命令执行的流程顺序.但是真正实现流程的工程是由插件来完成 ...
- 关于makefile中变量的多次赋值以及override指令
1 基本原则如下 1.1 原则1 变量的普通赋值是有先后顺序的,后面的赋值会覆盖掉前面的赋值. 1.2 原则2 使用的时候,用的是其前面最后的赋值,就算其后面有使用了override指令的赋值也不会影 ...
- 使用 Palette 让你的 UI 色彩与内容更贴合
版权声明: 本账号发布文章均来自公众号,承香墨影(cxmyDev),版权归承香墨影所有. 每周会统一更新到这里,如果喜欢,可关注公众号获取最新文章. 未经允许,不得转载. 一.前言 今天介绍一个 An ...
- Grails笔记二:grails 2.4.3版本下generate-*失效问题解析
最近在学grails框架,因为其敏捷性让我非常喜欢,不过有点让人恼怒的是也许因为grails框架太新了,所以关于grails的书籍很少,而且市面上的书籍大部分都是2007或者2009年的,官方文档又都 ...
- JavaScript实现隔行换颜色
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>& ...
- SQL 软解析和硬解析详解
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt329 当客户端进程,将SQL语句通过监听器发送到Oracle时, 会触发一个 ...
- Spring mvc 转发、重定向
spring控制器最后返回一个ModelAndView(urlName),其中urNamel可以是一个视图名称,由视图解析器负责解析后将响应流写回客户端;也可以通过redirect/forward:u ...