7.1 持久化技术简介
android主要提供了以下3种方式用于数据持久化功能:
1, 文件存储
2, SharedPreferences存储
3, 数据库存储
7.2 文件存储
7.2.1 将数据存储到文件中
第一步:在主Activity的layout中添加一个控件EditText.
第二步:在主Activity中修改如下代码。
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) # 二个模式,替换:MODE_PRIVATE, 追加:MODE_APPEND
val writer = BufferedWriter(OutputStreamWriter(output))
writer.use {
it.write(inputText)
}
} catch (e: IOException) {
e.printStackTrace()
}
}
第三步:使用Device File Explorer查看文件,路径如下:/data/data/com.example.[projectname]/file/
7.2.2 从文件中读取数据
第一步:在主Activity中修改如下代码。
# onCreate()中
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()
}
第二步:测试代码,打开关闭应用测试
7.3 SharedPreferences存储
7.3.1 将数据存储到SharedPreferences中
1, Context类中的getSharedPreferences()方法(默认MODE_PRIVATE)
# 查看路径在/data/data/com.example.[projectname]/shared_prefs/中
saveButton.setOnClickListener {
val editor = getSharedPreferences("data", Context.MODE_PRIVATE).edit()
editor.putString("name", "Tom")
editor.putInt("age", 28)
editor.putBoolean("married", false)
editor.apply()
}
2,Activity类中的getPreferences()方法
(与getSharedPreferences相似,不过,将当前Activity类名作为存储文件名字,如下所示)
saveButton.setOnClickListener {
val editor = getPreferences(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("MainActivity", "name is $name")
Log.d("MainActivity", "age is $age")
Log.d("MainActivity", "married is $married")
}
7.3.3 实现记住密码功能
第一步:创建一个登录界面。
第二步:在密码和账号的编辑框中,实现登录效果,如果成功,就将数据保存。
第三步:重启应用,如果有数据,就将数据恢复到编辑框中。
7.4 SQLite数据库存储
7.4.1 创建数据库
SQLiteOpenHlper()(抽象类)
有二个抽象方法:onCreate(),onUpgrade()
有二个实例方法:共同点,是返回一个对数据库进行读写操作的对象。
getReadableDatabase():如果满磁盘,返回的对象,只能只读。
getWritableDatabase():如查满磁盘,出现异常。
第一步:创建一个MyDatabaseHelper类。
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," +
"category_id integer)"
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) {}
}
第二步:在主Activityr的onCreate()中添加如下代码。(布局中,添加一个按钮)
val dbHelper = MyDatabaseHelper(this, "BookStore.db", 1) 第三个参数为版本号
createDatabase.setOnClickListener {
dbHelper.writableDatabase
}
第三步:启动应用,点击创建,查看db文件,路径如下:/data/data/com.example.[appname]/databases/目录下。
右击这个db文件,Save as,保存到目标目录。
第四步:安装Database Navigator,在file -> setings -> flugins -> maketplace中搜索安装重启android studio。
第五步:在这个 Database Navigator plugins中导入刚刚的db文件。步骤如下:
点击“加号”,在点击Database file中“加号”,选择db文件的路径,点击Ok,
在connection -> Schemas -> main -> Book -> columns中查看列名。
7.4.2 升级数据库
第一步:修改MyDatabaseHelper类。
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," +
"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 succeeded", Toast.LENGTH_SHORT).show()
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {}
db.execSQL("drop table if exists Book")
db.execSQL("drop table if exists Category")
create(db)
}
第二步:在主Activityr的onCreate()中修改如下代码。(通过版本号的大小,更新数据库,执行onUpgrade())
val dbHelper = MyDatabaseHelper(this, "BookStore.db", 2) 第三个参数为版本号
createDatabase.setOnClickListener {
dbHelper.writableDatabase
}
第三步:运行应用,点击按钮,即可更新表。
7.4.3 添加数据
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) // 插入第二条数据,第二参数是默认值。
}
7.4.4 更新数据
updateData.setOnClickListener {
val db = dbHelper.writableDatabase
val values = ContentValues()
values.put("price", 10.99)
val rows = db.update("Book", values, "name = ?", arrayOf("The Da Vinci Code"))
// 第三、四个参数表示更新的条件,第四个是Kotlin语言的数组创建方法
Toast.makeText(this, "rows is $rows", Toast.LENGTH_SHORT).show()
}
7.4.5 删除数据
deleteData.setOnClickListener {
val db = dbHelper.writableDatabase
db.delete("Book", "pages > ?", arrayOf("500")) //同上
}
7.5.6 查询数据
queryData.setOnClickListener {
val db = dbHelper.writableDatabase
// 查询Book表中所有的数据
// db.query() 参数详解:
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()
}
7.4.7 使用SQL操作数据库
// 通过DBHelper类获取一个读写的SQLiteDatabase对象
SQLiteDatabase db = dbHelper.getWritableDatabase()
// insert
db.execSQL("insert into user (id,name,age) values (?,?,?)",arrayOf(1,"张三",18))
// update
db.execSQL("update user set name=? where id=?",arrayOf("张三",1))
// delete
db.execSQL("delete from user where id=1")
// query
Cursor cursor = db.rawQuery("select * from user where id=?", arrayOf("1"))
7.5 SQLite数据库最佳实践
7.5.1 使用事务
# 创建一个
fun cvOf(vararg pairs: Pair) = ContentValues().apply {
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)
}
}
}
replaceData.setOnClickListener {
val db = dbHelper.writableDatabase
db.beginTransaction() // 开启事务
try {
db.delete("Book", null, null)
if (true) {
// 在这里手动抛出一个异常,让事务失败
throw NullPointerException()
}
val values = cvOf("name" to "Game of Thrones", "author" to "George Martin", "pages" to 720, "price" to 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," +
"author text," +
"price real," +
"pages integer," +
"name text," +
"category_id integer)" //第2个版本号加的
private val createCategory = "create table Category (" +
"id integer primary key autoincrement," +
"category_name text," +
"category_code integer)"
override fun onCreate(db: SQLiteDatabase) { //第1个版本号加的
db.execSQL(createBook)
db.execSQL(createCategory)
Toast.makeText(context, "Create succeeded", Toast.LENGTH_SHORT).show()
}
# 加版本判断的好处:当第0版,升级到第2版,会执行1,2个判断。当第1版升级到第2版,会执行第2个判断,版本都能正确的升级。
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")
}
}
}