1.1将手机储存在文件中
例:在数据回收前,储存数据
context openFileOutput()方法 文件操作模式主要有**MODE_PRIVATE**和**MODE_APPEND**
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun onDestroy() {
super.onDestroy()
val input=editText.text.toString()
save(input)
}
private fun save(inputText : String){
try {
val output=openFileOutput("data",Context.MODE_PRIVATE)
val write =BufferedWriter(OutputStreamWriter(output))
write.use {
it.write(inputText)
}
}catch (e:Exception){
e.printStackTrace()
}
}
}
注意这里使用了一个use函数,这是Kotlin提供的一个内置扩展函数,他会保证在Lambda表达式中的代码全部执行完之后自动将外层的流关闭。不用再写一个finally
使用Android studio右下角Device File Explorer工具可以查看/data/data/com.example.filepersistencetest/files/目录下已经生成一个data文件
1.2从文件中读取数据
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,"读取缓存成功",Toast.LENGTH_SHORT).show()
}
}
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: java.lang.Exception){
e.printStackTrace()
}
return content.toString()
}
这里读取数据使用了一个forEachLine函数,这是Kotlin提供的一个内置扩展函数
调用EditText setSelection()方法将广播移动到文本末尾以便于继续输入
2.1储存数据
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","Brrils")
editor.putInt("age",21)
editor.putBoolean("married",false)
editor.apply()
}
}
在data/data/com.example.sharedpreferencestest/shared_prefs/下生成一个data.xml文件
2.2读取数据
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)
Toast.makeText(this,"${name}已经${age}岁了",Toast.LENGTH_SHORT).show()
}
2.3登陆保存密码实战
activity_main.xml文件内
MainActivity中
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//
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","")
accountid.setText(account)
passwordid.setText(password)
rememberPass.isChecked=true
}
//点击登陆按钮
login.setOnClickListener {
val account =accountid.text.toString()
val password=passwordid.text.toString()
val edit=prefs.edit()
if (account=="123456"&&password=="123456"){
if(rememberPass.isChecked){
edit.putBoolean("remember_password",true)
edit.putString("account",account)
edit.putString("password",password)
}
Toast.makeText(this,"登陆成功,欢迎回来!",Toast.LENGTH_SHORT).show()
}else{
edit.clear()
Toast.makeText(this,"登陆失败,请检查账号密码!",Toast.LENGTH_SHORT).show()
}
edit.apply()
}
}
}
3.1创建数据库
Android为了更方便的管理数据库,专门提供了一个SQLiteOpenHelper帮助类
SQLiteHelper接收4个参数1.Context,2.数据库名,3.允许我们在查询数据时返回一个自定义的Cursor,一般传入null,4.表示当前数据库版本号,可用于对数据库升级
数据库文件会存放在 data/data/< packagename >/databases/目录下
class MyDatabaseHelper(val context: Context,name: String,verson:Int) : SQLiteOpenHelper(context,name,null,verson){
private val createBook = "Create table Book("+"id integer primary key autoincrement,"+"author text"+
"price real,"+"pages integer,"+"name text)"
override fun onCreate(db: SQLiteDatabase?) {
if (db != null) {
db.execSQL(createBook)
}
Toast.makeText(context,"Create succeeded",Toast.LENGTH_SHORT).show()
}
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
TODO("Not yet implemented")
}
}
integer表示整型
real表示浮点型
text表示文本类型
blob表示二进制类型
修改MainActivity中的代码
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 }
}
}
3.2升级数据库
在MyDatabaseHelper中添加几行代码
class MyDatabaseHelper(val context: Context,name: String,verson:Int) : SQLiteOpenHelper(context,name,null,verson){
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)
db?.let{
it.execSQL(createBook)
it.execSQL(createCategory)
}
Toast.makeText(context,"Create succeeded",Toast.LENGTH_SHORT).show()
}
//升级功能
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
db?.let {
//如果存在Book和Category表,就将两个表删除掉
it.execSQL("drop table if exists Book")
it.execSQL("drop table if exists Category")
}
onCreate(db)
}
}
再修改MainActivity中的版本参数
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val dbHelper = MyDatabaseHelper(this,"BookStore.db",7)
createDatabase.setOnClickListener {
dbHelper.writableDatabase }
}
}
3.3插入数据
添加新按钮,在MainActivity中添加代码
...
addData.setOnClickListener {
val db=dbHelper.writableDatabase
val value1=ContentValues().apply {
put("name","The Da Vinci Code")
put("author","Dan Brown")
put("pages", 454)
}
db.insert("Book",null,value1)
val value2=ContentValues().apply {
put("name","The Lost Symbol")
put("author","Dan Brown")
put("pages", 510)
}
db.insert("Book",null,value2)
}
...
3.4更新数据
添加新按钮,在MainActivity中添加代码
...
updateData.setOnClickListener {
val db=dbHelper.writableDatabase
val values=ContentValues()
values.put("pages",455)
db.update("Book",values,"name=?", arrayOf("The Da Vinci Code"))
}
...
3.5删除数据
...
deleteData.setOnClickListener {
val db=dbHelper.writableDatabase
db.delete("Book","pages > ?", arrayOf("500"))
}
...
3.6查询数据
...
queryData.setOnClickListener {
val db=dbHelper.writableDatabase
//查询Book表中所有数据
val cursor = db.query("Book",null,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.getInt(cursor.getColumnIndex("pages"))
Log.d("MainActivity","书名: $name 作者:$author 有 $pages 页")
}while (cursor.moveToNext())
}
cursor.close()
}
...
调用moveToFirst()将数据指针移动到第一行位置
query()方法参数
1.table | fromtable_name | 指定查询的表名
2.columns | select column1,column2 指定查询的列名
3.selection | where column=value | 指定where的约束条件
4.selectionArgs | - | 为where中的占位符提供具体的值
5.groupBy | group by column | 指定需要group by的列
**6.having | having column = value | 对group by后的结果进一步约束 **
7.orderBy | order by column1,colum2 | 指定查询结果的排序方式
3.7使用SQL操作数据库
添加数据
db.execSQL("insert into Book(name , author , pages , price) values(?,?,?,?)",arrayOf("A","aa","66","16.96"))
更新数据
db.execSQL("update Book set price=? where name = ? ",arrayOf("10.99,"The Da)")
删除数据
db.execSQL("delete from Book where pages > ?" , arrayOf("500"))
查询数据
val cursor = db.rawQuery("select * from Book",null)
3.8使用事务
...
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)
}
db.insert("Book",null,values)
db.setTransactionSuccessful()//事务已经执行成功
}catch (e : Exception){
e.printStackTrace()
}finally {
db.endTransaction() //结束事务
}
}
...
3.9升级数据库的最佳写法
每个版本的数据库都会对应一个版本号,当指定的数据库版本号大于当前数据库版本号的时候,就会进入onUpgrade()方法。
修改MyDatabaseHelper
class MyDatabaseHelper(val context: Context,name: String,verson:Int) : SQLiteOpenHelper(context,name,null,verson){
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)
db?.let{
it.execSQL(createBook)
it.execSQL(createCategory)
}
Toast.makeText(context,"Create succeeded",Toast.LENGTH_SHORT).show()
}
//升级功能
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")
}
onCreate(db)
}
}
我们在Book表的建表语句中添加了一个category_id列,如果覆盖安装,就会进入升级数据库的操作中,在onUpgrade()方法中。
4.1简化SharedPreferences
用高阶函数简化SharedPreferences的用法
fun sharedPreferences.open(block : SharedPreferences.Editor.() -> Unit){
val editor = edit()
editor.block()
editor.apply()
}
定义好了open函数以后,我们可以在项目中使用SharedPreferences储存数据
getSharedPreferences("data",Context.MODE_PRIVE).open{
putString("name","Tom")
putInt("age",18)
putBoolean("married",false)
}
在Google提供的KTX扩展库中已经包含了上述SharePreferences方法,这个扩展库会在Android Studio创建项目的时候自动引入build.gradle的dependencies中
因此我们可以直接在项目中使用如下方法向sharedPreferences储存数据
getSharedPreferences("data",Context.MODE_PRIVE).edit{
putString("name","Tom")
putInt("age",18)
putBoolean("married",false)
}
4.2简化ContentValues的用法
新建一个ContentValues.kt文件,然后定义一个cvOf()方法
fun cvOf(vararg pairs : Pair) : ContentValues{
val cv = ContentValues()
for(par 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)
}
}
}
首先cvOf()方法接收了一个Pair参数,也就是使用A to B语法结构创造出来的参数类型。在参数前面加一个vararg关键字,vararg关键字对应的就是java中可变参数列表。
ContentValues的值可以有多种类型,所以我们需要将pair值的泛型指定成Any?,因为Any是Kotlin中所有类的共同基类,相当于Java的Object,而Any?则表示可以传入空值
结合高阶函数进一步优化
fun cvOf(vararg pairs : Pair) = ContentValues().apply{
for(par 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)
}
}
}
有了这个cvOf()方法之后,我们可以这样使用
val values = cvOf("name" to "Game of Thrones" , "author" to "George Martin" , "pages" to 720,"price" to 20.85)
KTX库中也提供了一个具有同样功能的方法
val values = contentValuesOf("name" to "Game of Thrones" , "author" to "George Martin" , "pages" to 720,"price" to 20.85)
db.insert("Book",null,values)