第一行代码 第三版 第七章 持久化存储

第 7 章 数据持久化

7.2 文件存储

7.2.1 将数据存储到文件中

存储简单文本数据或二进制数据

  1. 放个EditText

  2. 代码

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    override fun onDestroy() {
        super.onDestroy()
        val inputText = editText.text.toString()
        save(inputText)
    }

    private fun save(inputText: String) {
        try{
            val output = openFileOutput("data", Context.MODE_PRIVATE)//Context.MODE_PRIVATE 覆盖原本文件内容或创建新文件,Context.MODE_APPEND 如果已有文件,追加内容
            val writer = BufferedWriter(OutputStreamWriter(output))
            writer.use { //内置拓展函数,在Lambda表达式中代码全部执行完毕之后自动将外层的流关闭
                it.write(inputText)
            }
        }catch (e:IOException){
            e.printStackTrace()
        }
    }
}
  1. 在/data/data/包名/目录

7.2.2 从文件中读数据

val inputText = load()
if (inputText.isNotEmpty()) {
    editText.setText(inputText)
    editText.setSelection(inputText.length)//设置游标的位置
    Toast.makeText(this,"Restoring succeeded",Toast.LENGTH_SHORT).show()
}

 private fun load():String {
        val content = StringBuilder()
        try{
            val input  = openFileInput("data")
            val reader = BufferedReader(InputStreamReader(input))
            reader.use{
                reader.forEachLine { //内置拓展函数,将得到的每一行数据都回调到Lambda表达式
                    content.append(it)
                }
            }
        }catch (e:IOException){
            e.printStackTrace()
        }
        return content.toString()
    }

7.3 SharedPreferences 存储

7.3.1 将数据存储到 SharedPreferences 中

  • Context getSharePreferences
  • Activitity getPreferences
saveButton.setOnClickListener { 
    val editor = getSharedPreferences("data",Context.MODE_PRIVATE).edit()
    editor.putString("name", "Tom")
    editor.putInt("age",28)
    editor.putBoolean("married", false)
    editor.apply()
}

7.3.2 从 SharedPreferences 中读取数据

restoreButton.setOnClickListener {
    val prefs = getSharedPreferences("data", Context.MODE_PRIVATE)
    val name = prefs.getString("name","")
    val age = prefs.getInt("age", 0)
    val married = prefs.getBoolean("married", false)
    Log.d(TAG, "name is $name")
    Log.d(TAG, "age is $age")
    Log.d(TAG, "married is $married")
}

7.3.3 实现记住密码的功能

在广播的最佳实践基础上进行

  1. 增加勾选框
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    >
    <CheckBox
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/rememberPass"></CheckBox>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:text="Remember password"
        ></TextView>
</LinearLayout>
  1. 代码
val prefs = getPreferences(Context.MODE_PRIVATE)
val isRemember = prefs.getBoolean("remember_password", false)
if (isRemember) {
    val account =  prefs.getString("account","")
    val password = prefs.getString("password","")
    accountEdit.setText(account)
    passwordEdit.setText(password)
    rememberPass.isChecked = true
}
login.setOnClickListener {
    val account = accountEdit.text.toString()
    val password = passwordEdit.text.toString()
    if(account == "admin" && password == "123456"){
        val editor = prefs.edit()
        if (rememberPass.isChecked) {
            editor.putBoolean("remember_password", true)
            editor.putString("account", account)
            editor.putString("password", password)
        }else{
            editor.clear()
        }
        editor.apply()
        val intent = Intent(this, MainActivity::class.java)
        startActivity(intent)
        finish()
    }else{
        Toast.makeText(this,"account or password is invalid",Toast.LENGTH_SHORT).show()
    }
}

7.4 SQLite 数据库存储

7.4.1 创建数据库

  1. 实现 SQLiteOpenHelper 抽象类,重写create和upgrade抽象方法,分别用来创建和更新数据库
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,"+//将id设置为主键,自增长
            "author text,"+
            "price real,"+//浮点型
            "pages integer,"+
            "name text)"

    override fun onCreate(p0: SQLiteDatabase) {
        p0.execSQL(createBook)
        Toast.makeText(context,"Created succeeded",Toast.LENGTH_SHORT).show()
    }

    override fun onUpgrade(p0: SQLiteDatabase?, p1: Int, p2: Int) {
    }
}
  1. 编写创建数据库的代码
val dbHelper = MyDatabaseHelper(this,"BookStore.db",1)//数据库名和版本号
createDatabase.setOnClickListener { dbHelper.writableDatabase//创建数据库 }
  1. 利用插件查看创建的数据库
  • 首先找到数据库的文件地址 save as 导出
  • 下载Database Navigator
  • 加号 - SQLite -修改文件地址为刚刚导出的地方

7.4.2 升级数据库

  1. 代码
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,"+//将id设置为主键,自增长
            "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(p0: SQLiteDatabase) {
        p0.execSQL(createBook)
        p0.execSQL(createCategory)//需要新创建的数据库
        Toast.makeText(context,"Created succeeded",Toast.LENGTH_SHORT).show()
    }

    override fun onUpgrade(p0: SQLiteDatabase, p1: Int, p2: Int) {
    //将之前的表删除
        p0.execSQL("drop table if exists Book")
        p0.execSQL("drop table if exists Category")
        //重新调用创建语句
        onCreate(p0)
    }


}
  1. 升级版本号
val dbHelper = MyDatabaseHelper(this,"BookStore.db",3)

7.4.3 添加数据

addData.setOnClickListener {
		//获取数据库对象
    val db = dbHelper.writableDatabase
    //给ContentValue赋值
    val value1 = ContentValues().apply {
        put("name","The Da Vinci Code")
        put("author","Dan Brown")
        put("pages",454)
        put("price",16.96)
    }
    //插入数据
    db.insert("Book",null,value1)

    val value2 = ContentValues().apply {
        put("name","The Lost Symbol")
        put("author","Dan Brown")
        put("pages",510)
        put("price",19.95)
    }
    db.insert("Book",null,value2)

}

查看添加数据:重新存储数据库文件,然后利用插件重新获取,双击表,点击no filter

7.4.4 更新数据

updateData.setOnClickListener {
            val db = dbHelper.writableDatabase
            val values = ContentValues()
            values.put("price",10.99)
            db.update("Book",values,"name = ?", arrayOf("The Da Vinci Code"))//kotlin快捷创建数组
        }
//这个表中,名字为The Da Vinci Code的书价格都改为10.99

补充:可以不用插件,直接在App inspection中可以查看表的数据

7.4.5 删除数据

deleteData.setOnClickListener {
    val db = dbHelper.writableDatabase
    db.delete("Book","pages > ?", arrayOf("500"))
}
//页数大于500全都删除

7.4.6 查询数据

  • Query 七个参数
  1. table 表名

  2. columns 查询哪几列

  3. selection 4. selectionArgs 约束查询某一行或某几行

  4. groupBy 指定需要group by的列

  5. having 上面操作后的进一步过滤

  6. orderBy 指定排序方式,不指定则使用默认的排序

  • 返回值 Cursor
queryData.setOnClickListener {
    val db = dbHelper.writableDatabase
    //查询表中的所有数据
    val cursor = db.query("Book", null, null, null, null, null, null)
    if (cursor.moveToFirst()) {
        do {
            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, "book name is $name")
            Log.d(TAG, "book author is $author")
            Log.d(TAG, "book pages is $pages")
            Log.d(TAG, "book price is $price")
        }while (cursor.moveToNext())

    }
    cursor.close()
}

7.5.1 使用事务

事务:保证让一系列操作要么全部执行,要么都不执行

replaceData.setOnClickListener {
            val db = dbHelper.writableDatabase
            db.beginTransaction()
            try {
                db.delete("Book",null,null)
                if (true) {
                    //手动抛出异常让事务失败
//                    throw NullPointerException()
                    val values = ContentValues().apply {
                        put("name", "Game of Thrones")
                        put("author", "George Martin")
                        put("pages", 720)
                        put("price",20.85)
                    }
                    db.insert("Book", null, values)
                    db.setTransactionSuccessful()
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }finally {
                db.endTransaction()
            }
        }

7.5.2 升级数据库的最佳写法

  • 第一版
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,"+//将id设置为主键,自增长
            "author text,"+
            "price real,"+//浮点型
            "pages integer,"+
            "name text)"

    override fun onCreate(p0: SQLiteDatabase) {
        p0.execSQL(createBook)
    }
    
    override fun onUpgrade(p0: SQLiteDatabase, p1: Int, p2: Int) {
    }

}
  • 第二版 需要添加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,"+//将id设置为主键,自增长
            "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(p0: SQLiteDatabase) {
        p0.execSQL(createBook)
        p0.execSQL(createCategory)
     
    }

    //如果使用版本二覆盖版本一,就会创建一张新表
    override fun onUpgrade(p0: SQLiteDatabase, oldVersion: Int, p2: Int) {
        if(oldVersion <=1){
            p0.execSQL(createCategory)
        }
    }
}
  • 第三版 需要Book表中添加category_id字段
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,"+//将id设置为主键,自增长
            "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(p0: SQLiteDatabase) {
        p0.execSQL(createBook)
        p0.execSQL(createCategory)

    }

    //如果使用版本二覆盖版本一,就会创建一张新表
    override fun onUpgrade(p0: SQLiteDatabase, oldVersion: Int, p2: Int) {
        if(oldVersion <=1){
            //如果当前是第一版更新第三版,两个都会执行
            p0.execSQL(createCategory)
        }
        if (oldVersion <= 2) {
            //如果当前是第二版更新第三版,就会执行这个创建列
            p0.execSQL("alter table Book add column category_id integer")
        }
    }

}

7.6 Kotlin课堂:高阶函数的应用

7.6.1 简化 SharedPreferences 的用法

最初写法

val editor = getSharedPreferences("data",Context,MODE_PRIVATE).edit()
editor.putString("name","Tom")
editor.apply()
  1. 定义顶层方法 SharePreferences.kt
fun SharedPreferences.open(block: SharedPreferences.Editor.() -> Unit) {
    val editor = edit()
    editor.block()
    editor.apply()
}
  1. 调用
getSharedPreferences("data", Context.MODE_PRIVATE).open {
    putString("name","Tom")
}
  1. 这个实现在KTX拓展库中已经有了
getSharedPreferences("data", Context.MODE_PRIVATE).edit {
    putString("name","Tom")
}

7.6.2 简化 ContentValues 的用法

  • 最初写法
val values = ContentBalues()
values.put("name","Game of Thrones")
db.insert("Book",null,values)
  • 补充 mapOf函数允许使用 A to B 的方式来快捷创建一个键值对,返回值是Pair 对象
/**Pair是A to B产生的类型
 * Any是所有类的基类,相当于java中的Object
 * ?值可以为空
 * vararg修饰表示允许向方法传递多个Pair类型的参数
 */
fun cvOf(vararg pairs: Pair<String,Any?>):ContentValues {
    val cv = ContentValues()
    for (pair in pairs) {
        val key = pair.first
        val value = pair.second
        when (value) {
            is Int -> cv.put(key,value)
            is Long -> cv.put(key,value)
            is Short -> cv.put(key,value)
            is Float -> cv.put(key,value)
            is Double -> cv.put(key,value)
            is Boolean -> cv.put(key,value)
            is String -> cv.put(key,value)
            is Byte -> cv.put(key,value)
            is ByteArray -> cv.put(key,value)
            null -> cv.putNull(key)
        }
    }
    return cv
}
  • 调用
val values = cvOf("name" to "Game of Thrones","author" to "George Martin","pages" to 720,"price" to 20.85)
db.insert("Book",null,values)
  • 进一步优化
//apply的返回值就是调用对象
//单行代码函数语法糖,等号代替返回值的声明
fun cvOf(vararg pairs: Pair<String,Any?>)= ContentValues().apply {
    val cv = ContentValues()
    for (pair in pairs) {
        val key = pair.first
        val value = pair.second
        when (value) {
            is Int -> put(key,value)
            is Long -> put(key,value)
            is Short -> put(key,value)
            is Float -> put(key,value)
            is Double -> put(key,value)
            is Boolean -> put(key,value)
            is String -> put(key,value)
            is Byte -> put(key,value)
            is ByteArray -> put(key,value)
            null -> putNull(key)
        }
    }
}
  • 系统自带的库
val values = contentValuesOf("name" to "Game of Thrones","author" to "George Martin","pages" to 720,"price" to 20.85)
db.insert("Book",null,values)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小蒋的学习笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值