Android笔记——数据持久化技术
Android系统中主要提供了3种方式用于简单地实现数据持久化功能:文件存储、SharedPreferences存储以及数据库存储。
1.0文件存储
文件存储是Android中最基本的数据存储方式,它不对存储的内容进行任何格式化处理,所有数据都是原封不动地保存到文件当中的,因而它比较适合存储一些简单的文本数据或二进制数据。如果你想使用文件存储的方式来保存一些较为复杂的结构化数据,就需要定义一套自己的格式规范,方便之后将数据从文件中重新解析出来。
1.1将数据存储到文件中
Context类中提供了一个openFileOutput()方法,可以用于将数据存储到指定的文件中。
- 第一个参数是文件名,在文件创建的时候使用,注意这里指定的文件名不可以包含路径,因为所有的文件都默认存储到
/data/data/<package name>/files/
目录下; - 第二个参数是文件的操作模式,主要有MODE_PRIVATE和MODE_APPEND两种模式可选,默认是MODE_PRIVATE,表示当指定相同文件名的时候,所写入的内容将会覆盖原文件中的内容,而MODE_APPEND则表示如果该文件已存在,就往文件里面追加内容,不存在就创建新文件。其实文件的操作模式本来还有另外两种:MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE。这两种模式表示允许其他应用程序对我们程序中的文件进行读写操作,不过由于这两种模式过于危险,很容易引起应用的安全漏洞,已在Android 4.2版本中被废弃。
openFileOutput()方法返回的是一个FileOutputStream对象,得到这个对象之后就可以使用Java流的方式将数据写入文件中了。代码如下:
将数据存储到文件中创建的文件如下:
创建的文件
1.2从文件中读取数据
Context类中提供了一个openFileInput()方法,用于从文件中读取数据。它只接收一个参数,即要读取的文件名,然后系统会自动到/data/data/<package name>/files/
目录下加载这个文件,并返回一个FileInputStream对象,得到这个对象之后,再通过流的方式就可以将数据读取出来了。代码如下:
2.0SharedPreferences存储
不同于文件的存储方式,SharedPreferences是使用键值对的方式来存储数据的,并且SharedPreferences还支持多种不同的数据类型存储。
2.1将数据存储到SharedPreferences中
使用SharedPreferences存储数据,首先需要获取SharedPreferences对象。Android中主要提供了以下两种方法用于得到SharedPreferences对象。
- Context类中的getSharedPreferences()方法
- 第一个参数用于指定SharedPreferences文件的名称,如果指定的文件不存在则会创建一个,SharedPreferences文件都是存放在
/data/data/<package name>/shared_prefs/
目录下的; - 第二个参数用于指定操作模式,目前只有默认的MODE_PRIVATE这一种模式可选,它和直接传入0的效果是相同的,表示只有当前的应用程序才可以对这个SharedPreferences文件进行读写。
其他几种操作模式均已被废弃,MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE这两种模式是在Android 4.2版本中被废弃的,MODE_MULTI_PROCESS模式是在Android 6.0版本中被废弃的。
- Activity类中的getPreferences()方法
这个方法和Context中的getSharedPreferences()方法很相似,不过它只接收一个操作模式参数,因为使用这个方法时会自动将当前Activity的类名作为SharedPreferences的文件名。
得到了SharedPreferences对象之后,就可以开始向SharedPreferences文件中存储数据了,主要可以分为3步实现。
(1) 调用SharedPreferences对象的edit()方法获取一个SharedPreferences.Editor对象。
(2) 向SharedPreferences.Editor对象中添加数据,比如添加一个布尔型数据就使用putBoolean()方法,添加一个字符串则使用putString()方法,以此类推。
(3) 调用apply()方法将添加的数据提交,从而完成数据存储操作。
代码如下:
数据存储 data.xml文件
2.2从SharedPreferences中读取数据
SharedPreferences对象中提供了一系列的get方法,用于读取存储的数据,每种get方法都对应了SharedPreferences.Editor中的一种put方法,比如读取一个布尔型数据就使用getBoolean()方法,读取一个字符串就使用getString()方法。
这些get方法都接收两个参数:
- 第一个参数是键,传入存储数据时使用的键就可以得到相应的值了;
- 第二个参数是默认值,即表示当传入的键找不到对应的值时会以什么样的默认值进行返回。
3.0SQLite数据库存储
Android为了让我们能够更加方便地管理数据库,专门提供了一个SQLiteOpenHelper帮助类。
- SQLiteOpenHelper中有两个抽象方法:onCreate()和onUpgrade()。必须在自己的帮助类里重写这两个方法,然后分别在这两个方法中实现创建和升级数据库的逻辑。
- SQLiteOpenHelper中有两个非常重要的实例方法:getReadableDatabase()和getWritableDatabase()。这两个方法都可以创建或打开一个现有的数据库(如果数据库已存在则直接打开,否则要创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。不同的是,当数据库不可写入的时候(如磁盘空间已满),getReadableDatabase()方法返回的对象将以只读的方-式打开数据库,而getWritableDatabase()方法则将出现异常。
- SQLiteOpenHelper中有两个构造方法可供重写,一般使用参数少一点的那个构造方法即可。这个构造方法中接收4个参数:
第一个参数是Context;
第二个参数是数据库名,创建数据库时使用的就是这里指定的名称;
第三个参数允许我们在查询数据的时候返回一个自定义的Cursor,一般传入null即可;
第四个参数表示当前数据库的版本号,可用于对数据库进行升级操作。
构建出SQLiteOpenHelper的实例之后,再调用它的getReadableDatabase()或getWritableDatabase()方法就能够创建数据库了,数据库文件会存放在/data/data/<package name>/databases/
目录下。此时,重写的onCreate()方法也会得到执行,所以通常会在这里处理一些创建表的逻辑。
3.1创建数据库
创建按钮 继承自SQLiteOpenHelper 生成的BookStore.db文件这个目录下还存在另外一个BookStore.db-journal文件,这是一个为了让数据库能够支持事务而产生的临时日志文件,通常情况下这个文件的大小是0字节。
3.2升级数据库
MyDatabaseHelper中onUpgrade()方法是用于对数据库进行升级的。
在onUpgrade()方法中执行了两条DROP语句,如果发现数据库中已经存在Book表或Category表,就将这两张表删除,然后调用onCreate()方法重新创建。
升级数据库
升级数据库
之前传入的是1,现在只要传入一个比1大的数,就可以让onUpgrade()方法得到执行了。这里将数据库版本号指定为2,表示对数据库进行升级了。
Android提供了一系列的辅助性方法,让你在Android中即使不用编写SQL语句,也能轻松完成所有的CRUD操作。
3.3添加数据
SQLiteDatabase中提供了一个insert()方法,专门用于添加数据。它接收3个参数:
第一个参数是表名;
第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL,一般用不到这个功能,直接传入null即可;
第三个参数是一个ContentValues对象,它提供了一系列的put()方法重载,用于向ContentValues中添加数据,只需要将表中的每个列名以及相应的待添加数据传入即可。
3.4更新数据
SQLiteDatabase中提供了一个update()方法,用于对数据进行更新。这个方法接收4个参数:
第一个参数是表名,指定更新哪张表里的数据;
第二个参数是ContentValues对象,要把更新数据在这里组装进去;
第三、第四个参数用于约束更新某一行或某几行中的数据,不指定的话默认会更新所有行。
3.5删除数据
SQLiteDatabase中提供了一个delete()方法,专门用于删除数据。这个方法接收3个参数:
第一个参数仍然是表名;
第二、第三个参数用于约束删除某一行或某几行的数据,不指定的话默认会删除所有行。
删除数据
3.6查询数据
SQLiteDatabase中提供了一个query()方法用于对数据进行查询。这个方法的参数非常复杂,最短的一个方法重载也需要传入7个参数。
第一个参数是表名,表示我们希望从哪张表中查询数据。
第二个参数用于指定去查询哪几列,如果不指定则默认查询所有列。
第三、第四个参数用于约束查询某一行或某几行的数据,不指定则默认查询所有行的数据。
第五个参数用于指定需要去group by的列,不指定则表示不对查询结果进行group by操作。
第六个参数用于对group by之后的数据进行进一步的过滤,不指定则表示不进行过滤。
第七个参数用于指定查询结果的排序方式,不指定则表示使用默认的排序方式。
调用query()方法后会返回一个Cursor对象,查询到的所有数据都将从这个对象中取出。
查询数据
3.7使用SQL操作数据库
Android提供了一系列的方法,使得可以直接通过SQL来操作数据库。
SQL操作数据库
除了查询数据的时候调用的是SQLiteDatabase的rawQuery()方法,其他操作都是调用的execSQL()方法。
3.8使用事务
SQLite数据库是支持事务的,事务的特性可以保证让一系列的操作要么全部完成,要么一个都不会完成。
使用事务
上述代码就是Android中事务的标准用法,
首先调用SQLiteDatabase的beginTransaction()方法开启一个事务,
然后在一个异常捕获的代码块中执行具体的数据库操作,当所有的操作都完成之后,调用setTransactionSuccessful()表示事务已经执行成功了,
最后在finally代码块中调用endTransaction()结束事务。
注意观察,我们在删除旧数据的操作完成后手动抛出了一个NullPointerException,这样添加新数据的代码就执行不到了。不过由于事务的存在,中途出现异常会导致事务的失败,此时旧数据应该是删除不掉的。