一、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, name, 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",null, "name=?",
new String[]{name}, null, null, null);
if(curosr.moveToFirst()){
result = true;
}
curosr.close(); // 释放掉结果集的游标
db.close();
}
return result;
}
//增加操作:
public boolean add(String name,String phone,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", null, 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 phone,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", "name=?", 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, null, null, null, null, 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, name, 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 selection, 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 selection,
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, null, null, 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,true, 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/"),null, null, null, "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, null, null, 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, null,"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"/>
链接: