【Android入门】4、数据持久化:文件、SharedPreferences 和 Sqlite

五、数据持久化

Android的数据持久化3种方式: 文件存储, SharedPreferences, 数据库存储

5.1 文件持久化

5.1.1 文件写入

默认写在/data/data/com.example.FilePersistenceTest/files/data中, 当按back键时即写入该文件

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)
            val writer = BufferedWriter(OutputStreamWriter(output))
            writer.use {
    
    
                it.write(inputText)
            }
        } catch (e: IOException) {
    
    
            e.printStackTrace()
        }
    }
}

5.1.2 文件读取

程序加载时, 读取到editText中

class MainActivity : AppCompatActivity() {
    
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        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 {
    
    
                    content.append(it)
                }
            }
        } catch (e: IOException) {
    
    
            e.printStackTrace()
        }
        return content.toString()
    }
}

5.2 SharedPreferences 持久化

是键值对的形式, 且保持数据结构(如整型, 如字符串等), 更易用

5.2.1 数据写入

有2种获取SharedPreferences的方式

  • 第一种是Context类中的getSharedPreferences()方法, 其有2个参数
    • 第1个参数是路径, 在/data/data//shared_prefs/
    • 第2个参数是操作模式, 默认是MODE_PRIVATE, 即只有当前应用程序才可对此读写
  • 第二种是Activity类的getPreferences()方法
    • 只有1个参数
    • 自动将当前activity雷鸣作为SharedPreferences的文件名
    • 调用方式如下3步骤
      • 调用SharedPreferences对象的edit()方法获取一个SharedPreferences.Editor对象
      • 向SharedPreferences.Editor对象中添加数据,比如添加一个布尔型数据就使用putBoolean()方法,添加一个字符串则使用putString()方法,以此类推。
      • 调用apply()方法将添加的数据提交,从而完成数据存储操作

5.2.2 数据读取

布局如下

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/saveButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Save Data" />

    <Button
        android:id="@+id/restoreButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Restore Data" />
</LinearLayout>
  • MainActivity如下
class MainActivity : AppCompatActivity() {
    
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        saveButton.setOnClickListener {
    
    
            val editor = getSharedPreferences("data", Context.MODE_PRIVATE).edit()
            editor.putString("name", "Tom")
            editor.putInt("age", 28)
            editor.putBoolean("married", false)
            editor.apply()
        }
        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("MainActivity", "name is $name")
            Log.d("MainActivity", "age is $age")
            Log.d("MainActivity", "married is $married")
        }
    }
}

通过点击按钮, 会在/data/data/com.example.sharedpreferencestest/shared_prefs/data.xml中存放如下数据

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="name">Tom</string>
    <boolean name="married" value="false" />
    <int name="age" value="28" />
</map>

点击load按钮, 即可加载数据, 在控制台打印如下

// 刚开始点击load按钮后的打印, 为空值
2022-08-12 09:56:59.452 3376-3376/com.example.sharedpreferencestest D/MainActivity: name is 
2022-08-12 09:56:59.452 3376-3376/com.example.sharedpreferencestest D/MainActivity: age is 0
2022-08-12 09:56:59.452 3376-3376/com.example.sharedpreferencestest D/MainActivity: married is false
// 点击save按钮后, 再点击load按钮后的打印, 为save的值
2022-08-12 10:04:05.721 5566-5566/com.example.sharedpreferencestest D/MainActivity: name is Tom
2022-08-12 10:04:05.721 5566-5566/com.example.sharedpreferencestest D/MainActivity: age is 28
2022-08-12 10:04:05.721 5566-5566/com.example.sharedpreferencestest D/MainActivity: married is false

在这里插入图片描述

6.2.3 实现记住密码功能

新建一个LoginActivity,布局如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:orientation="horizontal">

        <TextView
            android:layout_width="90dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:text="Account:"
            android:textSize="18sp" />

        <EditText
            android:id="@+id/accountEdit"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_weight="1" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:orientation="horizontal">

        <TextView
            android:layout_width="90dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:text="Password:"
            android:textSize="18sp" />

        <EditText
            android:id="@+id/passwordEdit"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_weight="1"
            android:inputType="textPassword" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <CheckBox
            android:id="@+id/rememberPass"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Remember password"
            android:textSize="18sp" />
    </LinearLayout>

    <Button
        android:id="@+id/login"
        android:layout_width="200dp"
        android:layout_height="60dp"
        android:layout_gravity="center_horizontal"
        android:text="Login" />
</LinearLayout>
  • LoginActivity如下
class LoginActivity : BaseActivity() {
    
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)
        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()
            // 如果账号是admin且密码是123456,就认为登录成功
            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()
            }
        }
    }
}

在这里插入图片描述

5.3 Sqlite 数据库持久化

5.3.1 创建数据库

SQLiteOpenHelper类, 有onCreate()和onUpgrade()方法, 实现数据库创建和升级
getReadableDatabase()和getWriteableDatabase()获取数据库实例
点击按钮则创建database, 其中databaseHelper专门负责调用sqliteOpenHelper来调用数据库

class MainActivity : AppCompatActivity() {
    
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val dbHelper = MyDatabaseHelper(this, "BookStore.db", 1)
        createDatabase.setOnClickListener {
    
    
            dbHelper.writableDatabase
        }
    }
}

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 succeeded", Toast.LENGTH_SHORT).show()
    }

    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
    
    
    }
}

在/data/data/com.example.databasetest/databases/BookStore.db有sqlite文件, 可导出并用DBBrowser插件查看

5.3.2 升级数据库

目前有了Book表,若希望增加Category表, 则可添加如下代码, 多次调用onCreate(), 但只有第一次才会生效, 因为只有第一次才会建库BookStore.db, 后续若此库存在则不会再建库BookStore.db, 除非卸载app或手动删除BookStore.db文件

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 succeeded", Toast.LENGTH_SHORT).show()
    }

    // 为了保证升级兼容性, 一般会在onUpgrade()方法内根据version判断执行不同版本的升级逻辑
    // 从低version开始, 保证任何版本都能成功执行
    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
    
    
         if (oldVersion <= 1) {
    
    
             db.execSQL(createCategory)
         }
         if (oldVersion <= 2) {
    
    
             db.execSQL("alter table Book add column category_id integer")
         }
    }
}

若传入的version大于旧值则调用onUpgrade, 否则若version相等则调onUpdate

val dbHelper = MyDatabaseHelper(this, "BookStore.db", 2)
// 传入的version>1时, 即会调用onUpgrade()

5.3.3 添加数据

通过如下, 可将数据插入数据库中

class MainActivity : AppCompatActivity() {
    
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val dbHelper = MyDatabaseHelper(this, "BookStore.db", 2)

        addData.setOnClickListener {
    
    
            val db = dbHelper.writableDatabase
            val values1 = ContentValues().apply {
    
    
                // 开始组装第一条数据
                put("name", "The Da Vinci Code")
                put("author", "Dan Brown")
                put("pages", 454)
                put("price", 16.96)
            }
            db.insert("Book", null, values1) // 插入第一条数据
            val values2 = ContentValues().apply {
    
    
                // 开始组装第二条数据
                put("name", "The Lost Symbol")
                put("author", "Dan Brown")
                put("pages", 510)
                put("price", 19.95)
            }
            db.insert("Book", null, values2) // 插入第二条数据
        }
    }
}

最终会发现落库成功

sqlite3 BookStore.db
SQLite version 3.35.4 2021-04-02 15:20:15
Enter ".help" for usage hints.
sqlite> select * from Book;
1|Dan Brown|16.96|454|The Da Vinci Code
2|Dan Brown|19.95|510|The Lost Symbol
3|Dan Brown|16.96|454|The Da Vinci Code
4|Dan Brown|19.95|510|The Lost Symbol

5.3.4 更新数据

class MainActivity : AppCompatActivity() {
    
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val dbHelper = MyDatabaseHelper(this, "BookStore.db", 2)
        updateData.setOnClickListener {
    
    
            val db = dbHelper.writableDatabase
            val values = ContentValues()
            values.put("price", 10.99)
            db.update("Book", values, "name = ?", arrayOf("The Da Vinci Code"))
        }
    }
}

更新后如下

sqlite3 BookStore.db
SQLite version 3.35.4 2021-04-02 15:20:15
Enter ".help" for usage hints.
sqlite> select * from Book;
1|Dan Brown|10.99|454|The Da Vinci Code
2|Dan Brown|19.95|510|The Lost Symbol
3|Dan Brown|10.99|454|The Da Vinci Code
4|Dan Brown|19.95|510|The Lost Symbol

5.3.5 删除数据

deleteData.setOnClickListener {
    
    
    val db = dbHelper.writableDatabase
    db.delete("Book", "pages > ?", arrayOf("500"))
}

5.3.6 查询数据

在这里插入图片描述

下例为获取表中所有行

queryData.setOnClickListener {
    
    
    val db = dbHelper.writableDatabase
    // 查询Book表中所有的数据
    val cursor = db.query("Book", null, null, null, null, null, null)
    if (cursor.moveToFirst()) {
    
    
        do {
    
    
            // 遍历Cursor对象,取出数据并打印
            val name = cursor.getString(cursor.getColumnIndex("name"))
            val author = cursor.getString(cursor.getColumnIndex("author"))
            val pages = cursor.getInt(cursor.getColumnIndex("pages"))
            val price = cursor.getDouble(cursor.getColumnIndex("price"))
            Log.d("MainActivity", "book name is $name")
            Log.d("MainActivity", "book author is $author")
            Log.d("MainActivity", "book pages is $pages")
            Log.d("MainActivity", "book price is $price")
        } while (cursor.moveToNext())
    }
    cursor.close()
}

查出后打印日志如下

2022-08-12 15:10:34.264 7666-7666/com.example.databasetest D/MainActivity: book name is The Da Vinci Code
2022-08-12 15:10:34.265 7666-7666/com.example.databasetest D/MainActivity: book author is Dan Brown
2022-08-12 15:10:34.265 7666-7666/com.example.databasetest D/MainActivity: book pages is 454
2022-08-12 15:10:34.265 7666-7666/com.example.databasetest D/MainActivity: book price is 10.99
2022-08-12 15:10:34.265 7666-7666/com.example.databasetest D/MainActivity: book name is The Lost Symbol
2022-08-12 15:10:34.265 7666-7666/com.example.databasetest D/MainActivity: book author is Dan Brown
2022-08-12 15:10:34.265 7666-7666/com.example.databasetest D/MainActivity: book pages is 510
2022-08-12 15:10:34.265 7666-7666/com.example.databasetest D/MainActivity: book price is 19.95
2022-08-12 15:10:34.265 7666-7666/com.example.databasetest D/MainActivity: book name is The Da Vinci Code
2022-08-12 15:10:34.265 7666-7666/com.example.databasetest D/MainActivity: book author is Dan Brown
2022-08-12 15:10:34.265 7666-7666/com.example.databasetest D/MainActivity: book pages is 454
2022-08-12 15:10:34.265 7666-7666/com.example.databasetest D/MainActivity: book price is 10.99
2022-08-12 15:10:34.265 7666-7666/com.example.databasetest D/MainActivity: book name is The Lost Symbol
2022-08-12 15:10:34.265 7666-7666/com.example.databasetest D/MainActivity: book author is Dan Brown
2022-08-12 15:10:34.265 7666-7666/com.example.databasetest D/MainActivity: book pages is 510
2022-08-12 15:10:34.265 7666-7666/com.example.databasetest D/MainActivity: book price is 19.95

5.3.7 直接用 SQL 操作数据库

db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)",
 arrayOf("The Da Vinci Code", "Dan Brown", "454", "16.96")
)
db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)",
 arrayOf("The Lost Symbol", "Dan Brown", "510", "19.95")
)
db.execSQL("update Book set price = ? where name = ?", arrayOf("10.99", "The Da Vinci Code"))
db.execSQL("delete from Book where pages > ?", arrayOf("500"))
val cursor = db.rawQuery("select * from Book", null)

5.3.8 使用事务

先开启事务, 然后在try catch 和 finally中捕获exception, 做对应处理

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() // 结束事务
    }
}

日志就会打印printStackTrace对应的方法

2022-08-12 15:30:39.075 8005-8005/com.example.databasetest W/System.err: java.lang.NullPointerException
2022-08-12 15:30:39.080 8005-8005/com.example.databasetest W/System.err:     at com.example.databasetest.MainActivity.onCreate$lambda-7(MainActivity.kt:71)
2022-08-12 15:30:39.080 8005-8005/com.example.databasetest W/System.err:     at com.example.databasetest.MainActivity.$r8$lambda$Ff3cxcjnoRHI0B8DD9nQh8YQ2cY(Unknown Source:0)

猜你喜欢

转载自blog.csdn.net/jiaoyangwm/article/details/127042486
今日推荐