上篇文章已经介绍了如何使用SharedPreferences存储键值对形式的轻量级数据,对于那些相同结构的多组数据,类似于存储Java中定义的类的多个对象属性值,如果按照键值对的形式一条条读写,需要分别定义每条数据对应的key值,是相当繁琐的。而如果可以使用数据库保存就会方便很多。

正因此,Android系统提供了对SQLite数据库的支持,在应用中创建的数据库,默认也是保存在应用程序的内部存储空间中的,这样也只有当前应用程序内部可以访问其数据库中数据。

使用纯粹的SQLiteDatabase类操作数据库

在Android系统中可以使用android.database.sqlite.SQLiteDatabase数据库类,直接操作SQLite数据库。同时借助android.database.sqlite.SQLiteOpenHelper数据库帮助类,来获取数这里的据库类。

定义数据库结构类

一般要保存的类结构与数据库结构保持一致即可,以学生信息为例,下面创建的Student类即可直接作为数据库结构类,只需按照Student类中的属性名一一对应,定义数据库中的字段名。

public final class Student {
private String name;
private String birthday;
private int level;
private Student() {} public static final String TABLE_NAME = "student";
public static final String COLUMN_ID = "_id";
public static final String COLUMN_NAME = "name";
public static final String COLUMN_BIRTHDAY = "birthday";
public static final String COLUMN_LEVEL = "level";
}

在定义数据库中的字段名时,通常会定义值为 _id 的字段作为数据表中的自增长字段,这是为了在读取数据表时使用android.widget.CursorAdapter适配器子类可以正常创建其实例化对象。否则在使用CursorAdapter适配器实例化对象时可能会抛出java.lang.IllegalArgumentException异常。

创建数据库

与数据库中包含数据表的结构相对应,这里定义继承自SQLiteOpenHelper的子类处理数据表间关系,并在自定义子类中实现onCreate(SQLiteDatabase db)onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)方法。

通常该类的构造方法继承自其父类SQLiteOpenHelper (Context context, String name, SQLiteDatabase.CursorFactory factory, int version),参数 context 为使用该数据库的上下文环境;参数 name 为数据库文件的名字;参数 factory 为访问数据库使用的游标工厂,通常传入 null 即可;参数 version 为数据表的版本号。

该类实现的onCreate(SQLiteDatabase db)方法会在对象创建后,数据库文件首次创建时调用,因此可以在该方法中执行数据表的创建操作,参数 db 即当前数据库对象,可调用该对象的相关方法操作数据库。

onUpdate(SQLiteDatabase db, int oldVersion, int newVersion)方法会在该类对象创建后,数据库文件存在但数据库版本升级时调用,因此在该方法中可以执行数据表的更新操作。其中参数 db 为当前数据库对象,同样调用该对象的相关方法可操作数据库;参数 oldVersion 为数据库版本升级之前的旧版本号;参数 newVersion 为数据库版本升级之后的新版本号。

以上文创建Student学生类对应的数据库为例,示例代码如下。当首次创建StudentDbHelper对象时,其对应数据库版本号为10,且数据库文件需要首次创建,因此会执行该对象的onCreate()方法,在数据库中执行SQL_CREATE_STUDENT定义的sql语句,创建不包含 birthday 字段的数据表。而如果在以后需要更新数据表时,想增加 birthday 字段,只需要在创建StudentDbHelper对象时,将其对应数据库版本号改为20,并在onUpdate()方法中做版本号的判断,一旦判断符合条件,即可执行SQL_ADD_BIRTHDAY定义的sql语句。

public class StudentDbHelper extends SQLiteOpenHelper {
public static final int DATABASE_VERSION_FIRST = 10;
public static final int DATABASE_VERSION_SECONDDATABASE_VERSION_SECOND = 20; public static final String DATABASE_NAME = "students.db"; private static final String SQL_CREATE_STUDENT =
"CREATE TABLE " + Student.TABLE_NAME + " (" +
Student.COLUMN_ID + " INTEGER PRIMARY KEY," +
Student.COLUMN_NAME + " TEXT," +
Student.COLUMN_LEVEL + " TEXT)"; private static final String SQL_ADD_BIRTHDAY =
"ALTER TABLE " + Student.TABLE_NAME +
" ADD COLUMN " + Student.COLUMN_BIRTHDAY + " TEXT"; public StudentDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION_FIRST);
//super(context, DATABASE_NAME, null, DATABASE_VERSION_SECOND);
} public void onCreate(SQLiteDatabase db) {
db.execSQL(SQL_CREATE_STUDENT);
} public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if(newVersion==DATABASE_VERSION_SECOND){
db.execSQL(SQL_DELETE_BIRTHDAY);
}
} }

操作数据库

上边在SQLiteDbHelper子类中已经对SQLiteDatabase类有所了解,除此之外,也可以在需要获取数据库对象的地方通过SQLiteDbHelper对象的getWritableDatabase()方法获取可读写数据库和getReadableDatabase()方法获取可读式数据库。

在获取到SQLiteDatabase数据库对象之后,可通过其相关方法分别执行数据库的增删改查等操作。

调用该对象的insert(String table, String nullColumnHack, ContentValues values)系列方法,可以在数据库中插入一条数据。返回 long 类型的结果表示插入数据在数据表中的id序列,如果插入失败则返回 -1 。其中参数 table 为要插入的数据表名;参数 nullColumnHack 可以指定要插入的字段名,通常为null时忽略,使用后边参数中所包含的字段数据;参数 values 指定要插入的数据,同样使用 key-value 键值对的形式存取数据。

调用该对象的delete(String table, String whereClause, String[] whereArgs)方法,可以删除数据库中指定的数据。返回 int 类型的结果表示删除的数据条数。其中参数 table 同样为要删除的数据表名;参数 whereClause 为指定删除条件,其符合sql语句,但变量参数可用?代替,在后边参数中指定具体参数值;参数 whereArgs 即为参数值数组,长度与参数 whereClause 中的?符合数量一致。如果删除某个数据表中的所有内容,只需将参数二和参数三均置为null即可。

调用该对象的update(String table, ContentValues values, String whereClause, String[] whereArgs)方法,可以更新数据库中指定的数据。返回 int 类型的结果表示更新的数据条数。其中参数 table 同样为要更新的数据表名;参数 values 指定要更新的字段数据;参数 whereClause 可以指定更新条件;参数 whereArgs 对应指定更新条件中的参数值。这里如果参数三和参数四均为null,则会更新数据表中所有数据条目。

调用该对象的query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)系列方法,可以查询数据库中的指定数据。返回android.database.Cursor类型的游标对象,可以暂存多条结果。其中参数 table 为要查询的数据表名;参数 columns 为返回的结果中所包含的字段,如果为null则返回所有字段;参数 selection 为查询条件,同样符合sql语句,但变量参数用?代替;参数 selectionArgs 对应于查询条件中变量参数的值所组成的数组;参数 groupBy 与sql语句中的 GROUP BY 一致,可以指定返回的数据中以某个字段为分组依据,如果为null则不会分组;参数 having 同样与sql语句中的 HAVING 一致,指定返回的数据中是否包含某个字段,如果为null则包含所有数据;参数 orderBy 同样与sql语句中的 ORDER BY 一致,可以指定根据某个字段排序,如果为null则不会排序;参数 limit 与sql语句中的 LIMIT 一致,可以分页查找。

对于query()方法返回的Cursor类型,可以使用isX()系列方法判断当前状态X,使用moveX()系列方法将当前游标移动到某条数据对应位置,使用getX()系列方法获取游标当前位置对应条目数据的各字段及对应值。在使用完游标结果之后,一定要使用close()方法关闭当前游标,否则在下次查询数据时将依然返回当前的游标结果。

以上四种增删改查对应的操作方法,都可以使用原生的sql语句实现,所以可以直接调用SQLiteDatabase对象的execSQL (String sql)方法,传入一条已经定义好的sql语句即可。该方法在上文创建数据库时已有使用示例,可支持大多数sql语句。

在数据库中有事务的概念,也就是将多个增删改查操作线性执行看成一个整体的操作。同样在Android中,通过调用SQLiteDatabase对象的begainTransaction()方法可以启动一次事务,在所有事务操作执行结束后,再调用setTransactionSuccessful()方法标志当前事务操作以完成,如果不调用该方法,当前事务即便被提交也不会执行。最终再调用endTransaction()方法以结束当前事务,并判断在调用上述setTransactionsuccessful()标志方法条件下提交并执行当前事务。

释放数据库资源

在对数据库的所有操作结束之后,一般是在使用SQLiteDbHelper对象所在的组件生命周期结束之前,要调用SQLiteDbHelper对象的close()方法,以释放应用程序对数据库的资源占有。

借助更便捷的Room框架

为了在开发中更聚焦于业务代码逻辑,而简化数据库的开关流程,像Room这种开发级框架也就应运而生了。Room框架是有官方提供的推荐框架,除此之外,还有其他开发者或组织提供的优秀框架,包括但不限于GreenDao、LitePal、OrmLite等。由于开发级框架使用便捷,虽性能各有优劣,但使用大同小异,这里不再赘述。


简单开发中对数据库的使用只集中在增删改查的简单操作中,尤其是查询数据往往取出结果后会做进一步的过滤处理,如果能在查询操作时巧妙的借助 GROUP BY 这种子语句,其效率定能更快一步,这里多是开发人员容易忽略的一点。总之合理使用数据库,对大量相同结构数据的存储是很高效的,同时如果想进一步提高数据库性能,建议多学习下sql语句相关内容。

Android系统编程入门系列之应用内数据保存数据库的更多相关文章

  1. Android系统编程入门系列之应用内键值对数据的简单保存

    在应用程序间及与用户的通信交互过程中,会产生并传递一系列数据.针对这些数据,有部分是只在应用程序中使用的缓存数据,还有一部分是在不同位置多次或长时间使用的持久化数据. 对于缓存数据来说,通常以代码中定 ...

  2. Android系统编程入门系列之加载界面Activity

    上回说到应用初始化加载及其生命周期,在Android系统调用Applicaiton.onCreate()之后,继续创建并加载清单文件中注册的首个界面即主Activity,也可称之为入口界面.主Acti ...

  3. Android系统编程入门系列之应用环境及开发环境介绍

        作为移动端操作系统,目前最新的Android 11.0已经发展的比较完善了,现在也到了系统的整理一番的时间,接下来的系列文章将以Android开发者为中心,争取用归纳总结的态度对初级入门者所应 ...

  4. Android系统编程入门系列之界面Activity绘制展示

    上篇文章介绍了界面Activity的启动方式和生命周期,本篇将继续介绍在界面Activity中的内容是如何绘制展示给用户的. 在Android系统上运行新创建的界面Activtiy,给用户展示的是空白 ...

  5. Android系统编程入门系列之界面Activity交互响应

    在上篇文章中已经了解到界面Activity的绘制完全依赖其加载的视图组件View,不仅如此,用户的每次触摸操作都可以在界面Activity内接收并响应,也可以直接传递给其中的某个视图View响应.本文 ...

  6. Android系统编程入门系列之界面Activity响应丝滑的传统动画

    上篇文章介绍了应用程序内对用户操作响应的相关方法位置,简单的响应逻辑可以是从一个界面Activity跳转到另一个界面Activity,也可以是某些视图View的相对变化.然而不管是启动一个界面执行新界 ...

  7. Android系统编程入门系列之界面Activity响应多元的属性动画

    在响应丝滑动画一篇文章中,分别介绍了作用于普通视图.绘制视图的绘制对象.和界面这三种对象的动画效果,但是都有一些使用的局限性.比如这些动画都只是以屏幕上绘制更新的方式绘制动画,并没有真实改变作用对象的 ...

  8. Android系统编程入门系列之加载服务Service

    之前几篇文章简单梳理了在Android系统的四大组件之一,最主要的界面Activity中,使应用程序与用户进行交互响应的相关知识点,那对于应用程序中不需要与用户交互的逻辑,又要用到哪些内容呢?本文开始 ...

  9. Android系统编程入门系列之服务Service齐头并进多线程任务

    在上篇文章中初步了解了Android系统的四大组件之一的服务Service,在服务内可以执行无用户交互的耗时操作任务,但是包括之前关于界面系列文章在内,生命周期方法都是在主线程内被系统回调的.如果直接 ...

随机推荐

  1. php header 设置文件下载

    控制器 php 代码: $filepath = "http://www.jiesen365.com/uploadfile/20150519044903-897.pdf"; //下载 ...

  2. friend靶机

    仅供个人娱乐 靶机信息 https://www.vulnhub.com/entry/me-and-my-girlfriend-1,409/ 一.主机探测 二.信息收集 访问一下web站点,提示只能从本 ...

  3. Adaptive AUTOSAR 学习笔记 12 - 通信管理

    本系列学习笔记基于 AUTOSAR Adaptive Platform 官方文档 R20-11 版本 AUTOSAR_EXP_PlatformDesign.pdf 缩写 CM:Communicatio ...

  4. SpringCloud升级之路2020.0.x版-8.理解 NamedContextFactory

    本系列为之前系列的整理重启版,随着项目的发展以及项目中的使用,之前系列里面很多东西发生了变化,并且还有一些东西之前系列并没有提到,所以重启这个系列重新整理下,欢迎各位留言交流,谢谢!~ spring- ...

  5. js 原始数据类型、引用数据类型

    js的数据类型划分方式为 原始数据类型和 引用数据类型 栈: 原始数据类型(Undefined,Null,Boolean,Number.String) 堆: 引用数据类型(对象.数组.函数) 两种类型 ...

  6. 1.9 货仓选址问题——Python

    题目描述 在一条数轴上有 N 家商店,它们的坐标分别为 A1~AN. 现在需要在数轴上建立一家货仓,每天清晨,从货仓到每家商店都要运送一车商品. 为了提高效率,求把货仓建在何处,可以使得货仓到每家商店 ...

  7. elk 7.9.3 版本容器化部署

    ELK-V7.9.3 部署 为什么用到ELK? 平时我们需要进行日志分析的时候,可以直接在日志文件中 grep.awk 就可以过滤出自己想要的信息及关键字,但规模较大的场景中,此方法极大的减低了效率, ...

  8. DVWA靶场之XSS(Reflected)通关

    反射型xss Low: <?php header ("X-XSS-Protection: 0"); // Is there any input? if( array_key_ ...

  9. 程序员作图工具和技巧,你 get 了么?

    分享程序员常用的画图软件和小技巧 大家好,我是鱼皮. 说实话,我觉得做个程序员挺好的.日常工作有很多,写代码.对需求.写方案等等,但我最爱画图:流程图.架构图.交互图.功能模块图.UML 类图.部署图 ...

  10. 计算文件的MD5值和sha256值

    1.计算文件的MD5值. 1)linux系统计算 MD5值:md5sum+文件名 sha256值:sha256su+文件名 2)windows系统计算 MD5值:利用Notepad++工具计算 sha ...