android 之数据持久化

2021-12-08  本文已影响0人  0246eafe46bd

持久化技术

数据持久化就是指将那些内存中的瞬时数据保存到存储设备中,保证即使在手机或计算机关机的情况下,这些数据仍然不会丢失

保存在内存中的数据是处于瞬时状态的,而保存在存储设备中的数据是处于持久状态的。持久化技术提供了一种机制,可以让数据在瞬时状态和持久状态之间进行转换

文件存储

文件存储是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()
    }
}
上一篇 下一篇

猜你喜欢

热点阅读