上篇文章已经介绍了如何使用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. js问题记录

    1.aixos请求响应302重定向时无法获取返回数据, 解决方法:在请求头中添加  headers: { 'X-Requested-With': 'XMLHttpRequest' },

  2. H5页面怎么跳转到公众号主页?看过来

    前言: 做公众号开发的小伙伴,可能会遇到这种需求: 在一个H5页面点击一个关注公众号按钮跳转到公众号主页. 听到这个需求的一瞬间,疑惑了!这不可能! 摸了摸高亮的额头!没办法,做还是要做的 开始上解决 ...

  3. java继承基础详解

    java继承基础详解 继承是一种由已存在的类型创建一个或多个子类的机制,即在现有类的基础上构建子类. 在java中使用关键字extends表示继承关系. 基本语法结构: 访问控制符 class 子类名 ...

  4. SpringCloud升级之路2020.0.x版-11.Log4j2 监控相关

    本系列代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford Log4j2 异步 ...

  5. 『Java』String类使用方法

    Java中的字符串 java.lang.String类表示字符串类,Java程序中所有字符串文字都可以看作实现该类的实例. 特点: 字符串不可变:字符串的值在创建后不能在发生改变 public cla ...

  6. SaToken学习笔记-01

    SaToken学习笔记-01 SaToken版本为1.18 如果有排版方面的错误,请查看:传送门 springboot集成 根据官网步骤maven导入依赖 <dependency> < ...

  7. Vue系列-05-项目2

    路飞学城项目 调整首页细节 固定头部 App.vue <style> body{ padding: 0; margin:0; margin-top: 80px; } </style& ...

  8. 带你读AI论文丨用于目标检测的高斯检测框与ProbIoU

    摘要:本文解读了<Gaussian Bounding Boxes and Probabilistic Intersection-over-Union for Object Detection&g ...

  9. NLP与深度学习(一)NLP任务流程

    1. 自然语言处理简介 根据工业界的估计,仅有21% 的数据是以结构化的形式展现的[1].在日常生活中,大量的数据是以文本.语音的方式产生(例如短信.微博.录音.聊天记录等等),这种方式是高度无结构化 ...

  10. NOIP 模拟 $15\; \text{影子}$

    题解 \(by\;zj\varphi\) 一道并查集的题 对于它路径上点权,我们可以转化一下:对于一个点,它在哪些路径上是最小的点权 那么我们排个序,从大到小加入点,每回加入时,将这个点与它所相连的且 ...