一、文件存储
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Type something here"
/>
</LinearLayout>
package com.jpc.filepersistencetest
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.EditText
import android.widget.Toast
import java.io.BufferedReader
import java.io.BufferedWriter
import java.io.InputStreamReader
import java.io.OutputStreamWriter
import java.lang.StringBuilder
class MainActivity : AppCompatActivity() {
private lateinit var editText: EditText
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
editText = findViewById<EditText>(R.id.editText)
val fileData = load()
if (fileData.isNotEmpty()) {
// 调用EditText的setText()方法将内容填充到EditText
//里,并调用setSelection()方法将输入光标移动到文本的末尾位置以便继续输入
editText.setText(fileData)
editText.setSelection(fileData.length)
Toast.makeText(this, "数据已恢复", Toast.LENGTH_SHORT).show()
}
}
// 在销毁时保存数据到文件中
// override fun onDestroy() {
// super.onDestroy()
// val inputText = editText.text.toString()
// save(inputText)
// }
private fun save(inputText: String){
// 保存数据到TXT文件中,文件名data
val output = openFileOutput("data", Context.MODE_PRIVATE)
val writer = BufferedWriter(OutputStreamWriter(output))
writer.use {
it.write(inputText)
}
}
// 从文件中加载数据
private fun load():String {
val builder = StringBuilder()
val input = openFileInput("data")
val reader = BufferedReader(InputStreamReader(input))
reader.use {
reader.forEachLine {
builder.append(it)
}
}
return builder.toString()
}
}
二、SharedPreferences存储
不同于文件的存储方式,SharedPreferences是使用键值对的方式来存储数据的。当保存一条数据的时候,需要给这条数据提供一个对应的键,这样在读取数据的时候就可以通过这个键把相应的值取出来。SharedPreferences还支持多种不同的数据类型存储,如果存储的数据类型是整型,那么读取出来的数据也是整型的;如果存储的数据是一个字符串,那么读取出来的数据仍然是字符串。
<Button
android:id="@+id/btn_save_SharedPreferences"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="保存数据"
/>
// 指定SharedPreferences的文件名为data,并得到了
//SharedPreferences.Editor对象。接着向这个对象中添加了3条不同类型的数据,最
//后调用apply()方法进行提交,从而完成了数据存储的操作
val btnSave: Button = findViewById<Button>(R.id.btn_save_SharedPreferences)
btnSave.setOnClickListener{
val editor = getSharedPreferences("data", Context.MODE_PRIVATE).edit()
editor.putString("name", "Tom")
editor.putInt("age", 28)
editor.putBoolean("married", false)
editor.apply()
}
<?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>
SharedPreferences对象中提供了一系列的get方法,用于读取存储的数据,每种get方法都对应了SharedPreferences.Editor中的一种put方法,比如读取一个布尔型数据就使用getBoolean()方法,读取一个字符串就使用getString()方法。这些get方法都接收两个参数:第一个参数是键,传入存储数据时使用的键就可以得到相应的值了;第二个参数是默认值,即表示当传入的键找不到对应的值时会以什么样的默认值进行返回。
val btnRestore = findViewById<Button>(R.id.btn_restore_SharedPreferences)
btnRestore.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)
Toast.makeText(this, "我的信息:${name}-${age}-${married}", Toast.LENGTH_SHORT).show()
}
1、记住密码功能
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<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:textSize="18sp"
android:text="Remember password" />
</LinearLayout>
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="Login" />
</LinearLayout>
class LoginActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
val prefs = getPreferences(Context.MODE_PRIVATE)
www.blogss.cn
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 {
// 如果没有被选中,就简单地调用一下clear()方法,
// 将SharedPreferences文件中的数据全部清除掉。
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()
}
}
}
}
三、 SQLite数据库存储
显然,文件存储和SharedPreferences存储只适用于保存一些简单的数据和键值对。
但是,Android系统是内置了数据库的,SQLite是一款轻量级的关系型数据库,它的运算速度非常快,占用资源很少,通常只需要几百KB的内存就足够了,因而特别适合在移动设备上使用。SQLite不仅支持标准的SQL语法,还遵循了数据库的ACID事务。
1、创建数据库
package com.jpc.filepersistencetest
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.widget.Toast
class MyDatabaseHelper(private val context: Context, name: String, version: Int):
SQLiteOpenHelper(context, name, null, version) {
// 建表语句
private val createBook = "create table Book (" +
"id integer primary key autoincrement," +
"name text," +
"author text," +
"price real," +
"pages integer)"
override fun onCreate(db: SQLiteDatabase?) {
db?.execSQL(createBook)
Toast.makeText(context, "建表成功", Toast.LENGTH_SHORT).show()
}
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
}
}
// 构建了一个MyDatabaseHelper对象,并且通过构造函数的参
//数将数据库名指定为BookStore.db,版本号指定为1,然后在“Create Database”按钮的点击
//事件里调用了getWritableDatabase()方法。
val btnCreateDataBase = findViewById<Button>(R.id.btn_create_database)
val databaseHelper = MyDatabaseHelper(this, "BookStore.db", 1)
btnCreateDataBase.setOnClickListener{
databaseHelper.writableDatabase
}
2、升级数据库
SQLiteOpenHelper的构造方法里接收的第四个参数,它表示当前数据库的版本号,之前我们传入的是1,现在只要传入
一个比1大的数,就可以让onUpgrade()方法得到执行了。
package com.jpc.filepersistencetest
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.widget.Toast
class MyDatabaseHelper(private val context: Context, name: String, version: Int):
SQLiteOpenHelper(context, name, null, version) {
// 建表语句
private val createBook = "create table Book (" +
"id integer primary key autoincrement," +
"name text," +
"author text," +
"price real," +
"pages 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, "建表成功", 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")
// 再重新创建
onCreate(db)
}
}
// 第一次创建数据库,版本version为1
// val databaseHelper = MyDatabaseHelper(this, "BookStore.db", 1)
// 升级数据库,将版本改为比上一次大的数值,就会调用onUpgrade方法
val databaseHelper = MyDatabaseHelper(this, "BookStore.db", 2)
btnCreateDataBase.setOnClickListener{
databaseHelper.writableDatabase
}
3、添加数据
调用SQLiteOpenHelper的getReadableDatabase()或
getWritableDatabase()方法是可以用于创建和升级数据库的,不仅如此,这两个方法还都会返回一个SQLiteDatabase对象,借助这个对象就可以对数据进行CRUD操作了。
SQLiteDatabase中提供了一个 insert() 方法,专门用于添加数据。它接收3个参数:第一个参数是表名,我们希望向哪张表里添加数据,这里就传入该表的名字;第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL,一般我们用不到这个功能,直接传入null即可;第三个参数是一个ContentValues对象,它提供了一系列的put()方法重载,用于向ContentValues中添加数据,只需要将表中的每个列名以及相应的待添加数据传入即可。
<Button
android:id="@+id/btn_add_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="向表添加数据"
/>
// 添加数据
val btnAddData = findViewById<Button>(R.id.btn_add_data)
btnAddData.setOnClickListener{
val db = databaseHelper.writableDatabase
val data1 = ContentValues().apply {
// 第一条数据
// 不需要给id赋值,因为id自增
put("name", "The Da Vinci Code")
put("author", "Dan Brown")
put("pages", 454)
put("price", 16.96)
}
// 插入第一条数据
db.insert("Book", null, data1)
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) // 插入第二条数据
}
4、修改数据
SQLiteDatabase中提供了一个非常好用的update()方法,用于对数据进行更新。这个方法接收4个参数:第一个参数和insert()方法一样,也是表名,指定更新哪张表里的数据;第二个参数是ContentValues对象,要把更新数据在这里组装进去;第三、第四个参数用于约束更新某一行或某几行中的数据,不指定的话默认会更新所有行。
// 修改数据
val btnUpdate = findViewById<Button>(R.id.btn_update_data)
btnUpdate.setOnClickListener{
val db = databaseHelper.writableDatabase
val contentValues = ContentValues()
contentValues.put("price", 10.99)
// 第三、第四个参数来指定具体更新
//哪几行。第三个参数对应的是SQL语句的where部分,表示更新所有name等于?的行,而?是一
//个占位符,可以通过第四个参数提供的一个字符串数组为第三个参数中的每个占位符指定相应
//的内容,arrayOf()方法是Kotlin提供的一种用于便捷创建数组的内置方法
db.update("Book", contentValues, "name = ?", arrayOf("The Da Vinci Code"))
}
5、删除数据
SQLiteDatabase中提供了一个delete()方法,专门用于删除数据。这个方法接收3个参数:第一个参数仍然是表名,这个没什么好说的;第二、第三个参数用于约束删除某一行或某几行的数据,不指定的话默认会删除所有行。
// 删除数据
val btnDelete = findViewById<Button>(R.id.btn_delete_data)
btnDelete.setOnClickListener{
val db = databaseHelper.writableDatabase
// 指定删除条件,即where字句,如果不指定就会删除表中所有的数据
db.delete("Book",