【android,4】4.SQLite数据库,内容提供者、内容观察者

前端之家收集整理的这篇文章主要介绍了【android,4】4.SQLite数据库,内容提供者、内容观察者前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

一、sqlist 数据库的介绍:

在Android平台上,集成了一个嵌入式关系型数据库sqlite,sqlite3支持 NULL、INTEGER、REAL(浮点数字)、TEXT(字符串文本)和BLOB(二进制对象)数据类型,虽然它支持的类型只有五种,但实际上sqlite3也接受varchar(n)、char(n)、decimal(p,s) 等数据类型,只不过在运算或保存时会转成对应的五种数据类型。 sqlite最大的特点是你可以把各种类型的数据保存到任何字段中,而不用关心字段声明的数据类型是什么。例如:可以在Integer类型的字段中存放字符串,或者在布尔型字段中存放浮点数,或者在字符型字段中存放日期型值。但有一种情况例外:定义为INTEGER PRIMARY KEY的字段只能存储64位整数, 当向这种字段保存除整数以外的数据时,将会产生错误。另外, sqlite 在解析CREATE TABLE 语句时,会忽略 CREATE TABLE 语句中跟在字段名后面的数据类型信息,如下面语句会忽略 name字段的类型信息:

二、操作数据库

1、创建数据库具体要求:

自定义一个类继承系统的sqliteOpenHelper类,

重写类的构造方法,参数如下。

数据库创建的目录:/data/当前应用程序/databases/数据库名称

类中的onCreate 和 onUpgrade方法 使用说明在例中。

public class PersonDBOpenHelper extends sqliteOpenHelper {

/**

* 数据库被创建的构造方法,

* 第一个参数 上下文

* 第二个参数 数据库名称

* 第三个参数 数据库查询结果的游标工厂 一般用系统默认的游标工厂即可

* 第四个参数 数据库的版本号 版本号>=1

* @param context

*/

publicPersonDBOpenHelper(Context context) {

super(context,"person.db",null,3);

}

/**当数据库第一次被创建的时候 会去执行

*

* 当第一次获取数据库的实例的时候 才会执行 并且创建这个数据库

* 一旦数据库创建出来,不会再去执行oncreate方法

*/

public void onCreate(sqliteDatabasedb) {

System.out.println("哈哈 数据库被创建了");

//执行sql语句,创建表。

db.execsql("CREATE TABLE person (personid integer primarykey autoincrement,name varchar(20))");

}

/**

* 当数据库的版本增长的时候 会被调用

* 一般应用场景 就是我们需要去更改数据库的表结构

*/

@Override

public voidonUpgrade(sqliteDatabase db,int oldVersion,int newVersion) {

System.out.println("数据库版本变化了");

db.execsql("ALTER TABLE person ADD phone VARCHAR(12) NULL");

}

}

2、实例化数据库对象:

//创建数据库类的对象

PersonDBOpenHelper dbhelper = new PersonDBOpenHelper(this);

//此方法执行时,但数据不存在时,才会创建数据库调用onCreate方法

dbhelper.getReadableDatabase();

三、数据库的增删改查操作:

1、下面是一个dao类,其中的CRUD操作。

public class PersonDao {

private PersonDBOpenHelper helper;

//构造函数传递一个上下文:

public PersonDao(Context context) {

helper = newPersonDBOpenHelper(context);//创建数据库类的对象。

}

//crud 增删改查

//增加操作:

public void add(String name,String phone){

if(find(name)){

return;

}

//得到可写的数据库实例

sqliteDatabase db =helper.getWritableDatabase();

if(db.isOpen()){//判断数据库是否打开

//执行插入语句:参数(sql语句,sql语句中参数值)

db.execsql("insertinto person (name,phone) values (?,?)",

new Object[]{name,phone});

db.close(); //养成习惯使用完毕后 关闭数据库

}

}

//查询的操作

public boolean find(String name){

boolean result = false;

//获取数据库只读的实例;

sqliteDatabase db = helper.getReadableDatabase();

if(db.isOpen()){

//获取结果集的游标

Cursor curosr =db.rawQuery("select * from person where name=?",new String[]{name});

if(curosr.moveToFirst()){

result = true;

}

curosr.close(); // 释放掉结果集的游标

db.close();

}

return result;

}

//更新 update

//update person set phone='119' where name='zhangsan' andphone='120'

public void update(String name,String phone,String newphone){

sqliteDatabase db =helper.getWritableDatabase();

if(db.isOpen()){

db.execsql("update person set phone=? where name=? andphone=?",new Object[]{newphone,name,phone});

db.close();

}

}

//删除数据

//delete from person where name='zhangsan'

public void delete(String name){

sqliteDatabase db =helper.getWritableDatabase();

if(db.isOpen()){

db.execsql("delete from person where name=?",newObject[]{name});

db.close();

}

}

//获取所有的数据

public List<Person> findAll(){

List<Person>persons = new ArrayList<Person>();

sqliteDatabase db = helper.getReadableDatabase();

if(db.isOpen()){

//获取游标对象,

Cursor cursor = db.rawQuery("select * from person",null);

while(cursor.moveToNext()){

//获取指定列名对应的值

int id = cursor.getInt(cursor.getColumnIndex("personid"));

String name =cursor.getString( cursor.getColumnIndex("name"));

String phone =cursor.getString(

cursor.getColumnIndex("phone"));

Person p = newPerson(id,phone);

persons.add(p);

}

cursor.close();

db.close();

}

return persons;

}

}

2、比较数据库的可写实例:

①、当需要更改数据时:获取可写实例

sqliteDatabase db =helper.getWritableDatabase();

②、当查询数据库时,获取只读实例:

sqliteDatabase db = helper.getReadableDatabase();

③、两个方法的区别:

获取的数据实例相同,

可写实例操作数据库时,会加锁,操作完以后释放锁。同一时刻只能有一个可写实例操作数据库

可读实例操作数据库时,可有1000千个可读实例同时操作数据库

三、使用封装的api操作数据库:

//查询的操作

//select * from person where name='zhangsan1'

public boolean find(String name){

boolean result = false;

sqliteDatabase db = dbhelper.getReadableDatabase();

if(db.isOpen()){

//参数1:表名,

//参数2:指定查询出来的列,为null时,返回所有列的信息,

//参数3:指定查询的条件,为null是,返回所有记录。

//参数4:指定第三个参数中占位符的值

//参数5:分组的语句,group by 后面的值。

//参数6:分组的条件 ,having后面的值,

//参数7:指定排序.

Cursor curosr = db.query("person","name=?",

new String[]{name},null);

if(curosr.moveToFirst()){

result = true;

}

curosr.close(); // 释放掉结果集的游标

db.close();

}

return result;

}

//增加操作:

public boolean add(String name,int account){

long result=0;

if(find(name)){

return false;

}

sqliteDatabase db =dbhelper.getWritableDatabase();

if(db.isOpen()){

//实质为map集合

ContentValues values = newContentValues();

values.put("name",name);//参数(列名,值)

values.put("phone",phone);

values.put("account",account);

//执行插入操作:参数(表名,当插入的所有值为null时防止拼接的sql语句出错的值,

要插入数据的键值对)

result = db.insert("person",values);

db.close(); //养成习惯使用完毕后 关闭数据库

}

//insert 方法的返回值为-1时,表示插入失败。

if(result==-1){

return false;

}else{

return true;

}

}

//更新 update

//update person set phone='119' where name='zhangsan' andphone='120'

public boolean update(String name,String newphone){

boolean result = false;

sqliteDatabase db =dbhelper.getWritableDatabase();

if(db.isOpen()){

ContentValues values = new ContentValues();

values.put("name",name);

values.put("phone",newphone);

//更行操作:

//参数1:表名,

//参数2:更新的数据

//参数3: where后面的选择条件

//参数4:选择条件中的值。

int raw = db.update("person",values,"name=? and phone=?",new String[]{name,phone});

//update 方法,返回操作的记录条数。

if(raw>0){

result = true;

}

db.close();

}

return result;

}

//删除数据

//delete from person where name='zhangsan'

public boolean delete(String name){

boolean result = false;

sqliteDatabase db =dbhelper.getWritableDatabase();

if(db.isOpen()){

//删除操作,参数(表名,查询条件,查询条件中的值)

int raw = db.delete("person",newString[]{name});

if(raw>0){

result = true;

}

db.close();

}

return result;

}

//获取所有的数据

public List<Person> findAll(){

List<Person>persons = new ArrayList<Person>();

sqliteDatabase db = dbhelper.getReadableDatabase();

if(db.isOpen()){

//查询获取游标对象:

//参数1:表名,

//参数2:指定查询出来的列,为null时,返回所有列的信息,

//参数3:指定查询的条件,为null是,返回所有记录。

//参数4:指定第三个参数中占位符的值

//参数5:分组的语句,group by 后面的值。

//参数6:分组的条件 ,having后面的值,

//参数7:指定排序.

Cursor cursor =db.query("person",null);

while(cursor.moveToNext()){

int id = cursor.getInt(cursor.getColumnIndex("personid"));

String name =cursor.getString( cursor.getColumnIndex("name"));

String phone =

cursor.getString( cursor.getColumnIndex("phone"));

Person p = newPerson(id,phone,100);

persons.add(p);

}

cursor.close();

db.close();

}

return persons;

}

四、sqlite中的事务:

android中默认是不开启事务。

使用sqliteDatabase的beginTransaction()方法可以开启一个事务,程序执行到endTransaction()方法时会检查事务的标志是否为成功,如果程序执行到endTransaction()之前调用了setTransactionSuccessful() 方法设置事务的标志为成功则提交事务,如果没有调用setTransactionSuccessful() 方法则回滚事务。

例:

public voidtestTransaction() throws Exception {

PersonDbOpenHelperhelper = new PersonDbOpenHelper(getContext());

sqliteDatabase db =helper.getWritableDatabase();

db.beginTransaction();//开始事务

try {

// 王五的账户减去100块钱

db.execsql("update person set account=account-100

where name='wangwu'");

// 李四2的账户里面增加100块钱

db.execsql("update person set account=account+100

where name='lisi2'");

db.setTransactionSuccessful(); //判断事务是否执行成功

} finally {

db.endTransaction();//结束事务

db.close();

}

}

五、内容提供者:ContentProvider

1、用于解决不同应用之间共享数据库的问题。当其他程序要访问A程序的数据库,需要通过A程序上的

内容提供者类,操作数据库内容提供中提供类CRUD 的方法

2、定义ContentProvider的使用:

①、自定义一个内容提供这类,继承ContentProvider类,

public class PersonProvider extends ContentProvider {

private static final intALL_PERSONS = 1;

private static final intPERSON = 2;

private static final intWANGWU = 3;

private static final intINSERT = 4;

private static final intDELETE = 5;

private static final intUPDATE = 6;

private static Uribaseuri = Uri.parse("content://cn.itcast.db.personprovider/");

private PersonDao dao;

// 创建一个uri的匹配器,如果匹配器没有找到对应的uri的类型 就返回 -1

private static UriMatchermather = new UriMatcher(UriMatcher.NO_MATCH);

static {

//addURI方法参数:

参数1:uri的主机名,取值为内容提供者在清单文件中配置的authorities属性对应的值,

参数2:匹配路径,

参数:匹配结果

mather.addURI("cn.itcast.db.personprovider","persons",ALL_PERSONS);

// cn.itcast.db.personprovider/persons 代表的是返回所有的person的信息

mather.addURI("cn.itcast.db.personprovider","person/#",PERSON);

// cn.itcast.db.personprovider/person/5 代表的是在person表中id为5的那个人的信息

mather.addURI("cn.itcast.db.personprovider","wangwu",WANGWU);

// cn.itcast.db.personprovider/wangwu 代表的是王五的信息

// 根据业务逻辑定义uri

mather.addURI("cn.itcast.db.personprovider","insert",INSERT);

// cn.itcast.db.personprovider/insert/ 代表的是往数据库里面添加一个信息

mather.addURI("cn.itcast.db.personprovider","delete",DELETE);

// cn.itcast.db.personprovider/delete/ 代表的是往数据库里面删除一个信息

mather.addURI("cn.itcast.db.personprovider","update",UPDATE);

// cn.itcast.db.personprovider/update/ 代表的是往数据库里面更新一个信息

}

/**

* 在PersonProvider 第一次被创建的时候执行

*/

@Override

public boolean onCreate(){

dao = new PersonDao(getContext());

return false;

}

// 返回cursor代表的数据的类型

@Override

public String getType(Uriuri) { //告诉调用者 返回的数据是一个集合 还是单一的一个条目

// .jpg mime

// .MP3

// .rmvb

int type =mather.match(uri);

switch (type) {

case ALL_PERSONS:

//vnd.android.cursor.dir/

return"vnd.android.cursor.dir/persons";

case PERSON:

return"vnd.android.cursor.item/person";

}

return null;

}

/**

* 通过uri 来表示要操作的数据 的路径

*/

@Override

public Cursor query(Uriuri,String[] projection,String selection,

String[] selectionArgs,String sortOrder) {

int type = mather.match(uri);

switch (type) {

// 如果地址匹配 cn.itcast.db.personprovider/persons

case ALL_PERSONS:

if (selection != null&& selectionArgs != null) {

return dao.find(selection,selectionArgs);

} else {

return dao.getCursor();

}

case PERSON: // cn.itcast.db.personprovider/person/#

//获取uri中最后的/后面的值,对应:请求的id。

long id =ContentUris.parseId(uri);

returndao.findPersonById(id);

case WANGWU: // cn.itcast.db.personprovider/wangwu

return dao.findWangwu();

default:

throw newIllegalArgumentException("uri 不能被识别");

}

}

@Override

public Uri insert(Uri uri,ContentValues values) {

if (mather.match(uri) == INSERT) {

long id =dao.add(values);

//content://cn.itcast.db.personprovider/person/id

//内容观察者

getContext().getContentResolver().notifyChange(baseuri,null);

returnContentUris.withAppendedId(

Uri.parse("content://cn.itcast.db.personprovider/person/"),

id);

} else {

throw newIllegalArgumentException("uri 不能被识别");

}

}

@Override

public int delete(Uriuri,String[] selectionArgs) {

if (mather.match(uri) == DELETE) {

boolean result =dao.delete(selection);

getContext().getContentResolver().notifyChange(baseuri,null);

if (result) {

return 1;

} else {

return -1;

}

} else {

throw newIllegalArgumentException("uri 不能被识别");

}

}

@Override

public int update(Uriuri,ContentValues values,

String[] selectionArgs){

if (mather.match(uri) == UPDATE) {

if (selectionArgs.length== 3) {

getContext().getContentResolver().notifyChange(baseuri,null);

boolean result =dao.update(selectionArgs[0],selectionArgs[1],

selectionArgs[2]);

if (result) {

return 1;

} else {

return -1;

}

}else{

throw new IllegalArgumentException("更新的参数传递不正确");

}

} else {

throw newIllegalArgumentException("uri 不能被识别");

}

}

}

②、contentprovider要在清单文件中声明。

activity 是应用程序的组件 要在清单文件里面声明

contentprovider 也是应用程序的组件 要在清单文件里面声明:

在AndroidManifest.xml文件的application元素中声明。

<application

android:icon="@drawable/ic_launcher"

android:label="@string/app_name">

<uses-libraryandroid:name="android.test.runner" />

//name 对应的是内容提供者的类路径

<providerandroid:name=".provider.PersonProvider"

//指定数据所在的地址。内容提供者的地址,一般为应用程序包名+内容提供者类名。

android:authorities="cn.itcast.db.personprovider"

></provider>

</application>

3、在其他程序中调用上面的内容提供者:

// 首先获取内容提供者的解析器

ContentResolver resolver =getContext().getContentResolver();

//定义访问内容提供者的uri 对象

Uri uri = Uri.parse("content://cn.itcast.db.personprovider/haha");

//调用内容提供者类中的query方法

Cursor cursor = resolver.query(uri,null);

六、内容提供者的特点:

1. 暴露自己应用程序私有的数据给别的应用程序

2. 屏蔽底层数据存储的细节,调用者不需要关心数据是怎么存储的,

只需要关心操作哪个uri,操作的数据是什么.

3. 内容提供不仅仅可以操作数据库,也可以操作别的文件,sp,网络.

4.内容提供者 还可以根据业务需求,只去增删改查中的某个方法.

七、内容观察者:检测内容提供者中的CRUD 方法是否被调用

1、在内容提供者的CRUD 方法添加如下代码:

private static Uri baseuri =Uri.parse("content://cn.itcast.db.personprovider/");

//notifyChange方法参数1:当内容提供者中的访问被调用时,向该参数指定的uri上发消息

参数2:内容观察者对象。

getContext().getContentResolver().notifyChange(baseuri,null);

2、在另外程序中,获取内容观察者:

registerContentObserver方法的参数1:对应观察者绑定的信息。

参数2:为true时表示观察者会观察uri及其子目录的信息的改变

参数3:是往baseuri 上注册内容观察者对象。

getContentResolver().registerContentObserver(baseuri,true,newMyObserver(new Handler()));

//定义一个内容观察者类;继承ContentObserver类。

private class MyObserver extends ContentObserver{

public MyObserver(Handler handler) {

super(handler);

}

//当上面的内容提供者的方crud方法被被调用,就会触发调用方法

public void onChange(boolean selfChange) {

super.onChange(selfChange);

System.out.println("数据发生改变啦");

}

}

八、短信监听器:

1、系统的短信的应用在com.android.mms

短信的内容 是存放在一个com.android.providers.telephony 目录下的mmsms.db里。

当mmsms.db 数据库中的内容发生改变时,就获取改变的信息。

在com.android.providers.telephony程序中已经注册了一个内容观察者。只需要获取所绑定的uri。

该uri 为 content://sms/ 。所以只要观察该uri即可做一个短信监听器。

2、注意:短信监听器操作短信时需要读短信和写短信的权限:

<uses-permissionandroid:name="android.permission.READ_SMS"/>

<uses-permissionandroid:name="android.permission.WRITE_SMS"/>

3、短息监听者的代码:

Uri uri =Uri.parse("content://sms/");

//注册观察者

getContentResolver().registerContentObserver(uri,new MyObserver(

new Handler()));

//定义一个观察者类

private class MyObserver extendsContentObserver{

public MyObserver(Handlerhandler) {

super(handler);

// TODO Auto-generated constructor stub

}

@Override

public voidonChange(boolean selfChange) {

super.onChange(selfChange);

System.out.println("短信内容发生改变啦");

//通过内容提供者获取数据。

Query方法参数1:uri类型,

参数2:要查询的列,为null时,表示所有列

参数3:查询条件,

参数4:查询条件里的参数值,

参数5:排序字段

Cursor cursor = getContentResolver().query(Uri.parse("content://sms/"),"date desc");

if(cursor.moveToFirst()){

String body =cursor.getString( cursor.getColumnIndex("body"));

System.out.println(body);

}

}

}

}

九、利用内容提供者获取联系人数据库:

1、联系人的信息是存放在com.android.providers.contacts 应用下的databases/contacts2.db数据库

2、contacts2.db数据库中三张重要的表:

①、raw_contacts 表: 字段:主键 _id,display_name

②、data表 字段:raw_contacts_id 关联raw_contacts 表中的_id.

data1字段 存放联系人的信息。

mimetype_id 关联:mimetype表中的主键_id.

③、mimetype表 :该表为数据类型表, 字段_id 主键,mimetype:存放的是数据类型。

添加一个联系人时就在raw_contacts表中添加一条记录,_id 主键,display_name字段存放联系人的名称

data表中添加三条信息 mimetype_id对应mimetype表中的主键。raw_contacts_id 对应raw_contacts表中主键_id.

3、查询表的步骤:

①、查询raw_contacts表 获取_id,通过_id关联data表中raw_contacts_id,查询出data表中的 mimetype_id,data1.再通过mimetype_id 关联mimetype表 的_id获取 mimetype对应的类型。

4、获取数据可中联系人的信息:

注意读取联系人的信息需要读的权限:

<uses-permissionandroid:name="android.permission.READ_CONTACTS"/>

//由源代码可知raw_contacts表的uri,获取内容提供者。

Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");

//获取结果集。

Cursor cursor = getContext().getContentResolver().query(uri,null);

while (cursor.moveToNext()) {

//获取id

String id =cursor.getString( cursor.getColumnIndex("_id"));

String name = cursor.getString(cursor.getColumnIndex("display_name"));

//System.out.println(id);

System.out.println("姓名"+ name);

//System.out.println("--");

//获取data表的uri

Uri dataUri =Uri.parse("content://com.android.contacts/data");

//通过条件raw_contacts.id = data.raw_contact_id获取结果集。

Cursor datacursor =getContext().getContentResolver().query(

dataUri,"raw_contact_id=?",new String[]{id},null);

while(datacursor.moveToNext()) {

//判断mimetype 等于vnd.android.cursor.item/phone_v2表示数据为电话号码。

if("vnd.android.cursor.item/phone_v2".equals(

datacursor.getString(datacursor.getColumnIndex("mimetype")))){

System.out.println(

"电话"+datacursor.getString(datacursor.getColumnIndex("data1")));

//类型email

}else if("vnd.android.cursor.item/email_v2".equals(

datacursor.getString(datacursor.getColumnIndex("mimetype")))){

System.out.println(

"邮箱"+datacursor.getString(datacursor.getColumnIndex("data1")));

}

}datacursor.close();

}

cursor.close();

5、向数据库中插入联系人信息:

①、插入操作:先向raw_contacts表中插入一条记录 ,然后再向data表中插入数据。

public void insertContacts(){

//获取raw_contacts的uri

Uri uri =Uri.parse("content://com.android.contacts/raw_contacts");

//创建插入到表中的ContentValue

ContentValues values = new ContentValues();

values.put("display_name","zhaoba");

Uri inserturi = getContext().getContentResolver().insert(uri,values);

//得到插入的数据 在数据库中的_id

long id = ContentUris.parseId(inserturi);

//data表的uri

Uri dataUri =Uri.parse("content://com.android.contacts/data");

//插入电话号码

ContentValues phonevalues = new ContentValues();

phonevalues.put("data1","7777");

phonevalues.put("raw_contact_id",id);

phonevalues.put("mimetype","vnd.android.cursor.item/phone_v2");//

getContext().getContentResolver().insert(dataUri,phonevalues);

//插入email

ContentValues emailvalues = new ContentValues();

emailvalues.put("data1","77777@itcast.cn");

emailvalues.put("raw_contact_id",id);

emailvalues.put("mimetype","vnd.android.cursor.item/email_v2");

getContext().getContentResolver().insert(dataUri,emailvalues);

//插入联系人姓名

ContentValues namevValues = new ContentValues();

namevValues.put("mimetype","vnd.android.cursor.item/name");

namevValues.put("raw_contact_id",id);

namevValues.put("data1","老方");

getContext().getContentResolver().insert(dataUri,namevValues);

}

}

注意:插入联系人需要写联系人的权限.

<uses-permissionandroid:name="android.permission.WRITE_CONTACTS"/>

猜你在找的Sqlite相关文章