持久化技术
数据持久化就是指将那些内存中的瞬时数据保存到存储设备中,保证即使在手机或计算机关机的情况下,这些数据仍然不会丢失
保存在内存中的数据是处于瞬时状态的,而保存在存储设备中的数据是处于持久状态的。持久化技术提供了一种机制,可以让数据在瞬时状态和持久状态之间进行转换
文件存储
文件存储是Android中最基本的数据存储方式,它不对存储的内容进行任何格式化处理,所有数据都是原封不动地保存到文件当中的,因而它比较适合存储一些简单的文本数据或二进制数据
将数据存储到文件中
Context.openFileOutput() 方法
public FileOutputStream openFileOutput(String name, int mode)
参数
name:文件名,在文件创建的时候使用,不可以包含路径,因为所有的文件都默认存储到/data/data/<package name>/files/目录下
mode:文件的操作模式,有MODE_PRIVATE和MODE_APPEND两种模式可选,默认是MODE_PRIVATE,表示当指定相同文件名的时候,所写入的内容将会覆盖原文件中的内容,而MODE_APPEND则表示如果该文件已存在,就往文件里面追加内容,不存在就创建新文件
示例
openFileOutput() 方法返回的是一个FileOutputStream对象,得到这个对象之后就可以使用Java流的方式将数据写入文件中了
openFileOutput("data.txt", Context.MODE_PRIVATE).use { fileOutputStream ->
fileOutputStream.bufferedWriter().use {
it.write("test openFileOutput")
}
}
use函数是Kotlin提供的一个内置扩展函数。它会保证在Lambda表达式中的代码全部执行完之后自动将外层的流关闭,这样就不需要我们再编写一个finally语句,手动去关闭流了
从文件中读取数据
Context.openFileInput() 方法
public FileInputStream openFileInput(String name)
参数
name:文件名,不可以包含路径,因为所有的文件都默认在/data/data/<package name>/files/目录下
示例
openFileInput() 方法返回的是一个FileInputStream对象,得到这个对象之后就可以使用 Java 流的方式读取数据了
openFileInput("data.txt").use { fileInputStream ->
fileInputStream.bufferedReader().use {
val readText = it.readText()
Log.d(TAG, "onCreate: readText=$readText")
}
}
SharedPreferences存储
SharedPreferences 是使用键值对的方式来存储数据的
将数据存储到SharedPreferences中
Context.getSharedPreferences() 方法
public SharedPreferences getSharedPreferences(String name, int mode)
参数
name:文件名,不存在则会创建一个,SharedPreferences文件都是存放在/data/data/<packagename>/shared_prefs/目录下
mode:操作模式,目前只有默认的MODE_PRIVATE这一种模式可选,它和直接传入0的效果是相同的,表示只有当前的应用程序才可以对这个SharedPreferences文件进行读写
Activity.getPreferences() 方法
和Context中的getSharedPreferences()方法很相似,不过它只接收一个操作模式参数,因为使用这个方法时会自动将当前Activity的类名作为SharedPreferences的文件名
示例
getSharedPreferences("shared_data", MODE_PRIVATE).apply {
edit().putString("name", "张三").putInt("age", 34).apply()
}
生成的文件 /data/data/com.example.androidstudy/shared_prefs/shared_data.xml 内容为
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="name">张三</string>
<int name="age" value="34" />
</map>
从SharedPreferences中读取数据
同样使用 Context.getSharedPreferences() 方法得到 SharedPreferences 对象,再调用其getXXX("键值")方法即可
示例
getSharedPreferences("shared_data", MODE_PRIVATE).apply {
val name = getString("name", "")
val age = getInt("age", 20)
Log.d(TAG, "onCreate: name=$name age=$age")
}
SQLite数据库存储
创建数据库
class MyDataBaseHelper(val context: Context, name: String, version: Int) :
SQLiteOpenHelper(context, name, null, version) {
private val createBook = "create table Book (" +
"id integer primary key autoincrement," +
"author text," +
"price real," +
"pages integer," +
"name text)"
override fun onCreate(db: SQLiteDatabase) {
db.execSQL(createBook)
Toast.makeText(context, "create table Book success", Toast.LENGTH_LONG).show()
}
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
}
}
在 MainActivity 中按钮点击的时候创建
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_database_main)
// 数据库名指定为BookStore.db,版本号指定为1
val dbHelper = MyDataBaseHelper(this, "BookStore.db", 1)
create_database.setOnClickListener {
// 检测到当前程序中并没有BookStore.db这个数据库
// 会创建该数据库并调用MyDatabaseHelper中的onCreate()方法
dbHelper.writableDatabase
}
}
}
Database Navigator 插件
下载 Database Navigator 插件,安装并重启后查看生成的数据库
image-20211209145041198.png
打开Device File Explorer,然后进入/data/data/com.example.databasetest/databases/目录下,可以看到已经存在了一个 BookStore.db 文件,对着BookStore.db文件右击→Save As,将它从模拟器导出到你的计算机的任意位置
image-20211209145425842.png
使用 Android Studio 的 DB Browser打开刚保存的 BookStore.db 文件
image-20211209145558420.png
image-20211209150130725.png
确定后就可以看到 DB Browser 中会显示出BookStore.db数据库里所有的内容
image-20211209150230935.png
升级数据库
MyDatabaseHelper.onUpgrade() 方法是用于对数据库进行升级的,例如:再添加一张Category表用于记录图书的分类
第一次升级
class MyDataBaseHelper(val context: Context, name: String, version: Int) :
SQLiteOpenHelper(context, name, null, version) {
private val createBook = "create table Book (" +
"id integer primary key autoincrement," +
"author text," +
"price real," +
"pages integer," +
"name text)"
private val createCategory = "create table Category (" +
"id integer primary key autoincrement," +
"category_name text," +
"category_code integer)"
override fun onCreate(db: SQLiteDatabase) {
db.execSQL(createBook)
db.execSQL(createCategory)
Toast.makeText(context, "create table Book success", Toast.LENGTH_LONG).show()
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
// 如果用户数据库的旧版本号小于等于1,就只会创建一张Category表
if (oldVersion <= 1) {
db.execSQL(createCategory)
}
}
}
在 MainActivity 中将数据库版本升高
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_database_main)
// 数据库名指定为BookStore.db,版本号指定为1,升级改为2
val dbHelper = MyDataBaseHelper(this, "BookStore.db", 2)
create_database.setOnClickListener {
// 检测到当前程序中并没有BookStore.db这个数据库
// 会创建该数据库并调用MyDatabaseHelper中的onCreate()方法
dbHelper.writableDatabase
}
}
}
第二次升级,在Book表中添加一个category_id字段
class MyDataBaseHelper(val context: Context, name: String, version: Int) :
SQLiteOpenHelper(context, name, null, version) {
// 在Book表的建表语句中添加了一个category_id列,
// 这样当用户直接安装第3版的程序时,这个新增的列就已经自动添加成功了
private val createBook = "create table Book (" +
"id integer primary key autoincrement," +
"author text," +
"price real," +
"pages integer," +
"name text," +
"category_id integer)"
private val createCategory = "create table Category (" +
"id integer primary key autoincrement," +
"category_name text," +
"category_code integer)"
override fun onCreate(db: SQLiteDatabase) {
db.execSQL(createBook)
db.execSQL(createCategory)
Toast.makeText(context, "create table Book success", Toast.LENGTH_LONG).show()
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
// 如果用户数据库的旧版本号小于等于1,就只会创建一张Category表
if (oldVersion <= 1) {
db.execSQL(createCategory)
}
//如果当前数据库的版本号是2,就会执行alter命令,为Book表新增一个category_id列
if (oldVersion <= 2) {
db.execSQL("alter table Book add column category_id integer")
}
}
}
每当升级一个数据库版本的时候,onUpgrade() 方法里都一定要写一个相应的if判断语句,这样可以保证数据库的表结构是最新的,而且表中的数据完全不会丢失
添加数据
在 MainActivity 的布局文件中加入一个添加数据的按钮,再起点击时,添加数据
add_data.setOnClickListener {
val db = dbHelper.writableDatabase
// 组装数据
val values1 = ContentValues().apply {
put("name", "Koltin Study")
put("author", "张三")
put("pages", 385)
put("price", 45.3)
}
val values2 = ContentValues().apply {
put("name", "Java Study")
put("author", "李四")
put("pages", 235)
put("price", 67.3)
}
// 向Book表中添加数据
db.insert("Book", null, values1)
db.insert("Book", null, values2)
}
双击 Book 表可以查看其中的数据
image-20211209155255840.png
更新数据
update_data.setOnClickListener {
val db = dbHelper.writableDatabase
val values = ContentValues().apply {
put("author", "李四四")
}
db.update("Book", values, "name = ?", arrayOf("Java Study"))
}
删除数据
delete_data.setOnClickListener {
val db = dbHelper.writableDatabase
db.delete("Book", "pages > ?", arrayOf("100"))
}
查询数据
query_data.setOnClickListener {
val db = dbHelper.writableDatabase
val cursor = db.query("Book", null, null, null, null, null, null)
while (cursor.moveToNext()) {
val name = cursor.getString(cursor.getColumnIndex("name"))
val author = cursor.getString(cursor.getColumnIndex("author"))
val pages = cursor.getString(cursor.getColumnIndex("pages"))
val price = cursor.getString(cursor.getColumnIndex("price"))
Log.d(TAG, "onCreate: name=$name author=$author pages=$pages price=$price ")
}
cursor.close()
}
使用SQL语句操作数据库
添加数据
db.execSQL(
"insert into Book (name,author,pages,price) values(?,?,?,?)",
arrayOf("OpenGL Stuudy", "王五", "324", "34.54")
)
更新数据
db.execSQL(
"update Book set price = ? where name = ?",
arrayOf("23.9", "Koltin Study")
)
删除数据
db.execSQL(
"delete from Book where pages > ?",
arrayOf("200")
)
查询数据
db.execSQL(
"select * from Book",
null
)
使用事务
假如Book表中的数据已经很老了,现在准备全部废弃,替换成新数据,可以先使用delete()方法将Book表中的数据删除,然后再使用insert()方法将新的数据添加到表中。我们要保证删除旧数据和添加新数据的操作必须一起完成,否则就要继续保留原来的旧数据
replace_data.setOnClickListener {
val db = dbHelper.writableDatabase
//开启事务
db.beginTransaction()
try {
db.delete("Book", null, null)
val values = ContentValues().apply {
put("name", "Jetpack Study")
put("author", "六六")
put("pages", 56)
put("price", 72.3)
}
db.insert("Book", null, values)
// 事务执行成功
db.setTransactionSuccessful()
} catch (e: Exception) {
e.printStackTrace()
} finally {
// 结束事务
db.endTransaction()
}
}








网友评论