本章我們介紹了數據存儲方案:文件存儲、SharedPreference和SQLite。文件存儲的讀寫,SharedPreference的讀寫和實現記住密碼功能;SQLite的創建升級數據庫以及CRUD;SQLite數據庫的最佳實踐:事務以及升級數據庫;最後我們講了利用高階函數和KTX庫簡化SharedPreference和ContentValues的寫法。
7.1.持久化技術簡介
保存於內存的數據是瞬時狀態,瞬時數據無法持久化保存。保存在存儲設備的數據是持久狀態的,有3種方式可以實現:文件存儲、SharedPreference存儲和數據庫存儲。
7.2.文件存儲
作用數據原封不動的保存到文件中,適用於存儲簡單地文本數據或二進制數據。
新建項目FilrPersistenceTest項目,使用Context類的openFileoutput方法可以將數據存儲至指定文件夾,openFileInput讀取指定的文件。MainActivity中的onDestroy方法中調用save方法,onCreate方法中調用load方法,代碼示例如下:
package com.example.myapplication
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_main.*
import java.io.*
import java.lang.StringBuilder
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val inputtext = load()
//如果讀到內容不爲空,setText填充後使用setSelection將光標移動到末尾位置
if (inputtext.isNotEmpty()) {
edit_text.setText(inputtext)
edit_text.setSelection(inputtext.length)
Toast.makeText(this, "Restore succeed", Toast.LENGTH_SHORT).show()
}
}
//銷燬之前調用了save方法用於存儲文本框內的數據
override fun onDestroy() {
super.onDestroy()
val inputText = edit_text.text.toString()
save(inputText)
}
fun save(inputText: String) {
try {
//Context提供openFileOutput方法,將數據存儲到指定文件夾,第一個參數是文件名,默認存儲至data/data/packagename/file目錄下
// 第二個參數是文件的操作模式,MODE_PRIVATE和MODE——APPEND,前者表示相同文件名,寫入內容覆蓋之前內容;後者該文件已存在,追加內容,不存在創建文件
//第一步:openFileOutput構建FileOutputStream對象
val output = openFileOutput("data", Context.MODE_PRIVATE)
//第二步:構建一個OutputStreamWriter對象後構造一個BufferWriter對象
val writer = BufferedWriter(OutputStreamWriter(output))
//第三步:通過bufferwriter開始將文本內容寫入文件當中
//Kotlin內置擴展函數use,他保證了代碼執行完成之後自動將外層流關閉,無需finally方法手動關閉流
writer.use {
it.write(inputText)
}
} catch (e: IOException) {
e.printStackTrace()
}
}
fun load(): String {
val content = StringBuilder()
try {
//第一步:openFileInput獲取FileInputStream對象
val input = openFileInput("data")
//第二步:藉助它構建InputStreamReader後構建BufferedReader對象
val reader = BufferedReader(InputStreamReader(input))
//第三步:forEachLine內置擴展函數,每行內容都會調到Lambda表達式並拼接
reader.use {
reader.forEachLine {
content.append(it)
}
}
} catch (e: IOException) {
e.printStackTrace()
}
return content.toString()
}
}
相應的xml文件:
<?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">
<!--界面上有一個文本框-->
<EditText
android:id="@+id/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Type sth" />
</LinearLayout>
7.3.SharedPreference存儲
文件存儲不適合存儲複雜結構型數據,SharedPreference使用鍵值對來存儲數據,讀取數據時根據鍵將值讀取出來,支持多種不同數據類型存儲。
7.3.1.將數據存儲到SharedPreference當中
主要通過Context的getSharedPreference方法獲取SharedPreference對象,調用edit()後獲取對象,putString等方法存儲鍵值對,最後使用apply方法進行提交。
7.3.2.從SharedPreference中讀取數據
很簡單,要通過Context的getSharedPreference方法獲取SharedPreference對象,調用getString等方法根據鍵獲取相應的值。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
save_button.setOnClickListener {
//第一步:獲取SharedPreference對象,有兩種方式:context類的getSharedPreferences和Activity類的getPreference方法,前者可以指定文件名,後者將Activity類名作爲文件名
//第二步:調用SharedPreference對象的editor方法獲取相應的對象
val editor = getSharedPreferences("data", Context.MODE_PRIVATE).edit()
//第三步:Editor對象添加數據,添加布爾類型數據、String類型等數據
editor.putString("name", "Tom")
editor.putInt("age", 20)
editor.putBoolean("married", false)
//第四步:apply方法將數據進行提交,完成數據庫存儲操作
editor.apply()
}
restore_button.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")
}
}
}
相對應的xml文件:
<?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">
<!--界面上有兩個點擊鍵-->
<Button
android:id="@+id/save_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Save Data" />
<Button
android:id="@+id/restore_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Restore Data" />
</LinearLayout>
7.3.3.基於SharedPreference實現記住密碼的功能
打開BroadcastBestPractice項目,修改佈局文件Activity_login.xml項目,複選框控件來表示用戶是否需要記住密碼:
<?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: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 Text"/>
</LinearLayout>
....
</LinearLayout>
修改LoginActivity,利用SharedPreference實現記住密碼的功能:
/**
* 功能:非常簡單的登錄功能,若成功,跳轉至MainActivity,否則提示賬號或者密碼錯誤
*/
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", "")
account_edit.setText(account)
password_edit.setText(password)
rememberPass.isChecked = true
}
login.setOnClickListener {
val account = account_edit.text.toString()
val password = password_edit.text.toString()
if (account == "admin" && password == "123") {
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 pass is invalid", Toast.LENGTH_SHORT).show()
}
}
}
}
7.4.SQLite數據庫存儲
SQLite是輕量級關係型數據庫,SharedPreference適用於存儲簡單數據和鍵值對,數據量大、結構性複雜的數據應當考慮使用SQLite數據庫。
7.4.1.創建數據庫
SQLiteOpenHelper是一個抽象類,用於簡單對數據庫進行創建和升級。創建DatabaseTest項目。新建MyDatabaseHelper類。
package com.example.myapplication
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.widget.Toast
//SQLiteHelper是一個抽象類,構造方法接收四個參數:context、數據庫名、返回自定義cursor一般爲null、數據庫版本號
//創建數據庫文件一般會放在data/data/packagename/databases/目錄下,一般需要複寫onCreate和onUpGrade方法
class MyDatabaseHelper(val context: Context, name: String, version: Int) :
SQLiteOpenHelper(context, name, null, version) {
//創建Book表,integer是正整型,real是浮點型,text是文本類型, blob是二進制類型。
//primary key將id列爲主鍵,autoincrement表示id列是自增長的。
private val createbooks = "create table Book(" +
"id integer primary key autoincrement," +
"author text," +
"prices real," +
"pages integer," +
"name text)"
override fun onCreate(db: SQLiteDatabase?) {
//執行建表語句
db?.execSQL(createbooks)
Toast.makeText(context, "Create succeed", Toast.LENGTH_SHORT).show()
}
override fun onUpgrade(db: SQLiteDatabase, oldversoin: Int, newversion: Int) {
TODO("Not yet implemented")
}
}
其次,修改佈局文件爲一個按鈕:
<?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">
<Button
android:id="@+id/create_db"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Create Database"/>
</LinearLayout>
最後修改MainActivity的方法,調用創建數據庫的getWritablebase方法。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//構造一個SQLiteOpenHelper對象,通過構造函數將數據庫指定爲book.db,版本號爲1.
val dbHelper = MyDatabaseHelper(this,"BookStore.db",1)
create_db.setOnClickListener{
//點擊後調用getWritableDatabase或者getReadableDatabase創建或打開一個現有的數據庫,若存在直接打開,否則創建新的。
//並返回一個可讀對象。
//若滿時,前者出現異常,後者返回可讀對象。
dbHelper.writableDatabase
}
}
}
7.4.2.升級數據庫
MyDatabaseHelper類複寫的onUpgrade方法用於對數據庫進行升級,已經有Book表,再添加category表用於記錄圖書分類。
三步走:1.建立SQL語句以創建category表,在onCreate方法中執行;2.在onUpgrade方法中添加刪表語句以及調用onCreate方法;3.爲使onUpgrade方法執行,SQLiteOpenHelper的數據庫版本號要比之前的大。
//SQLiteHelper是一個抽象類,構造方法接收四個參數:context、數據庫名、返回自定義cursor一般爲null、數據庫版本號
//創建數據庫文件一般會放在data/data/packagename/databases/目錄下,一般需要複寫onCreate和onUpGrade方法
class MyDatabaseHelper(val context: Context, name: String, version: Int) :
SQLiteOpenHelper(context, name, null, version) {
//創建Book表,integer是正整型,real是浮點型,text是文本類型, blob是二進制類型。
//primary key將id列爲主鍵,autoincrement表示id列是自增長的。
private val createbooks = "create table Book(" +
"id integer primary key autoincrement," +
"author text," +
"prices real," +
"pages integer," +
"name text)"
///創建記錄圖書分類的Castegory的SQL語句
private val createcategory = "create table Category(" +
"id integer primary key autoincrement," +
"category_name text," +
"category_code integer)"
override fun onCreate(db: SQLiteDatabase?) {
//執行建表語句
db?.execSQL(createbooks)
db?.execSQL(createcategory)
Toast.makeText(context, "Create succeed", Toast.LENGTH_SHORT).show()
}
//數據庫中已經存在Book表或者Category表,就將這兩條表刪除,重新執行onCreate方法創建
// 如果發現創建時已經存在此表,那麼會直接報錯、
override fun onUpgrade(db: SQLiteDatabase, oldversoin: Int, newversion: Int) {
db.execSQL("drop table if exists Book")
db.execSQL("drop table if exists Category")
onCreate(db)
}
}
//只要傳入比1大的數,就可以讓onUpgrade方法得到執行
val dbHelper = MyDatabaseHelper(this,"BookStore.db",2)
7.4.3.CRUD
CRUD分別指添加insert(create)、查詢select(retrive)、更新update(update)和刪除delete(delete)。前面提到getWritableDatabase或者getReadableDatabase返回一個SQLiteDataBase對象,藉助此可以進行CRUD操作。
佈局文件activity_main.xml。
<?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">
<Button
android:id="@+id/create_db"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Create Database"/>
<Button
android:id="@+id/add_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add Data"/>
<Button
android:id="@+id/update_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Update Data"/>
<Button
android:id="@+id/delete_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Delete Data"/>
<Button
android:id="@+id/query_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Query Data"/>
</LinearLayout>
修改MainActivity.java增加CRUD的操作:可以使用輔助性方法來完成操作數據庫的操作,也可以使用SQL來直接操作數據庫。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//構造一個SQLiteOpenHelper對象,通過構造函數將數據庫指定爲book.db,版本號爲1.
//只要傳入比1大的數,就可以讓onUpgrade方法得到執行
val dbHelper = MyDatabaseHelper(this, "BookStore.db", 2)
//實現創建數據庫的功能
create_db.setOnClickListener {
//點擊後調用getWritableDatabase或者getReadableDatabase創建或打開一個現有的數據庫,若存在直接打開,否則創建新的。
//並返回一個可讀對象。
//若滿時,前者出現異常,後者返回可讀對象。
dbHelper.writableDatabase
}
//實現添加數據的功能
add_data.setOnClickListener {
//獲取並返回DataBase對象
val db = dbHelper.writableDatabase
val values1 = ContentValues().apply {
put("name", "The Da Vinci Code")
put("author", "Dan Brown")
put("pages", 454)
put("prices", 16.96)
}
//insert方法用於向表中添加數據
//第一個參數爲表名,往那張表中添加;第二個參數用於指定哪列爲null,一般不使用;
// 第三個參數是ContentValue對象,提供一系列put方法添加數據。
db.insert("Book", null, values1)
val values2 = ContentValues().apply {
put("name", "The Lost Symbol")
put("author", "Dan Brown")
put("pages", 510)
put("prices", 19.95)
}
db.insert("Book", null, values2)
//直接使用SQL來操作數據庫,無需Android的SQLIte輔助性語言
db.execSQL(
"insert into Book(name ,author,pages,prices) values(?,?,?,?)",
arrayOf("The Da Vinci Code", "Dan Brown", "454", "16.96")
)
}
//實現更新數據的功能,將The Da Vinci Code書的價格調低一些
update_data.setOnClickListener {
val db = dbHelper.writableDatabase
val values = ContentValues()
values.put("prices", 10.99)
//update共接收四個參數,第一個是那個表,第二個是要更新的ContentValues數據;
// 第三、第四個參數是更新某一行或者某幾行的數據,不指定的話會默認更新所有行
//第三個對應的是SQL語句的where部分,表示更新所有name=?的部分,?是佔位符,第四個參數爲其提供內容
//arrayof是Kotlin提供的用於便捷創建數據的內置方法
db.update("Book", values, "name=?", arrayOf("The Da Vinci Code"))
db.execSQL(
"update Book set prices =? where name = ?",
arrayOf("10.99", "The Da Vinci Code")
)
}
//刪除Book表中超過500頁的書
delete_data.setOnClickListener {
val db = dbHelper.writableDatabase
//delete方法第一個參數是表明,第二、三個是用於約束刪除某一行或幾行的參數,不指定的話會刪除所有
db.delete("Book", "pages>?", arrayOf("500"))
db.execSQL("delete from Book where pages >?", arrayOf("500"))
}
//查詢Book表中的所有數據,並將其Log出來
query_data.setOnClickListener {
val db = dbHelper.writableDatabase
//query共七個參數,調用後可以返回一個cursor對象
val cursor = db.query("Book", null, null, null, null, null, null)
//將指針移動到第一行的位置後進入循環
if (cursor.moveToFirst()) {
//便利查詢到的每一行數據,通過Cursor的getColumnIndex去獲取某一列在表中對應位置的索引,然後通過索引來獲取數值。最後Log出來。
do {
//遍歷cursor對象
val name = cursor.getString(cursor.getColumnIndex("name"))
val author = cursor.getString(cursor.getColumnIndex("author"))
val pages = cursor.getString(cursor.getColumnIndex("pages"))
val prices = cursor.getString(cursor.getColumnIndex("prices"))
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 prices is $prices")
} while (cursor.moveToNext())
}
cursor.close()
val cursor1 = db.rawQuery("select * from Book", null)
cursor1.close()
}
}
}
7.5.SQLite數據庫的最佳實踐
7.5.1.使用事務
SQLite支持事務,事務特性是ACID:原子性(Atomicity)是操作這些指令時,要麼全部執行成功,要麼全部不執行;一致性(Consistency)是事務的執行使數據從一個狀態轉換爲另一個狀態,但是對於整個數據的完整性保持穩定;隔離性(Isolation)是當多個用戶併發訪問數據庫時,比如操作同一張表時,數據庫爲每一個用戶開啓的事務,不能被其他事務的操作所幹擾,多個併發事務之間要相互隔離。持久性(Durability)是當事務正確完成後,它對於數據的改變是永久性的。
在DatabaseTest項目基礎上修改,譬如Book表全部廢除替換成新數據,delete之後insert,要保證刪除舊數據和添加新數據的操作必須一起完成,否則繼續保留原來的舊數據。
replace_data.setOnClickListener {
val db = dbHelper.writableDatabase
db.beginTransaction()//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("prices", 20.85)
}
db.insert("Book", null, values)
//表示事務已經執行成功了
db.setTransactionSuccessful()
} catch (e: Exception) {
e.printStackTrace()
} finally {
//結束事務
db.endTransaction()
}
}
7.5.2.升級數據庫的最佳寫法
之前onupgrade有點粗暴,有沒有更好地數據庫升級方式?答案是有的。第二版是在只有Book表的基礎上再添加Category表;第三版是給Book表添加category_id列。充分考慮到跨版本升級與新版本安裝時的情況,代碼示例如下:
package com.example.myapplication
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.widget.Toast
//SQLiteHelper是一個抽象類,構造方法接收四個參數:context、數據庫名、返回自定義cursor一般爲null、數據庫版本號
//創建數據庫文件一般會放在data/data/packagename/databases/目錄下,一般需要複寫onCreate和onUpGrade方法
class MyDatabaseHelper(val context: Context, name: String, version: Int) :
SQLiteOpenHelper(context, name, null, version) {
//創建Book表,integer是正整型,real是浮點型,text是文本類型, blob是二進制類型。
//primary key將id列爲主鍵,autoincrement表示id列是自增長的。
private val createbooks = "create table Book(" +
"id integer primary key autoincrement," +
"author text," +
"prices real," +
"pages integer," +
"name text," +
"category_id integer)"
///創建記錄圖書分類的Castegory的SQL語句
private val createcategory = "create table Category(" +
"id integer primary key autoincrement," +
"category_name text," +
"category_code integer)"
override fun onCreate(db: SQLiteDatabase?) {
//執行建表語句
db?.execSQL(createbooks)
db?.execSQL(createcategory)
Toast.makeText(context, "Create succeed", Toast.LENGTH_SHORT).show()
}
//數據庫中已經存在Book表或者Category表,就將這兩條表刪除,重新執行onCreate方法創建
// 如果發現創建時已經存在此表,那麼會直接報錯、
override fun onUpgrade(db: SQLiteDatabase, oldversoin: Int, newversion: Int) {
// db.execSQL("drop table if exists Book")
// db.execSQL("drop table if exists Category")
// onCreate(db)
//第二版加入了添加category表的功能,無論版本是1、2都可以執行到。要充分考慮到第一版升到第二版或直接升到第三版的情況。
if (oldversoin <= 1) {
db.execSQL(createcategory)
}
//第三版加入了category_id列,無論版本號是1、2、3,都可以照顧到
if (oldversoin <= 2) {
db.execSQL("alter table Book add column category_id integer")
}
}
}
7.6.Kotlin課堂:高階函數的應用
7.6.1.簡化SharedPreference的用法
利用高階函數簡化SharedPreference的用法,之前的代碼如下:
//第1步:調用SharedPreference對象的editor方法獲取相應的對象
val editor = getSharedPreferences("data", Context.MODE_PRIVATE).edit()
//第2步:Editor對象添加數據,添加布爾類型數據、String類型等數據
editor.putString("name", "Tom")
editor.putInt("age", 20)
editor.putBoolean("married", false)
//第3步:apply方法將數據進行提交,完成數據庫存儲操作
editor.apply()
創建SharedPreference.kt文件:
package com.example.myapplication
import android.content.SharedPreferences
//SharedPreferences類中添加一個open函數,接收函數類型的參數。open函數內擁有SharedPre的上下文
fun SharedPreferences.open(block:SharedPreferences.Editor.() -> Unit){
//直接掉用edit來獲取SharedPreference.Editor對象。
val editor = edit()
//open函數接受的是SharedPreferences.Editor的函數類型參數,調用editor.block()對函數類型參數進行調用
editor.block()
//最後提交數據
editor.apply()
}
接着使用SharedPreference調用高階函數存儲數據:
//直接在SharedPreference對象上調用open函數,接着在Lambda表達式中完成數據的添加操作
getSharedPreferences("data", Context.MODE_PRIVATE).open {
//此時擁有了SharedPreference.Editor的上下文環境,只需要調用相應的put方法即可。
putString("name", "Tom")
putInt("age", 28)
putBoolean("married", false)
}
7.6.2.簡化ContentValues的用法
ContentValues的普通寫法:
val values2 = ContentValues()
values2.put("name", "The Lost Symbol")
values2.put("author", "Dan Brown")
values2.put("pages", 510)
values2.put("prices", 19.95)
db.insert("Book", null, values2)
//ContentValues寫法利用apply簡化:
val values2 = ContentValues().apply {
put("name", "The Lost Symbol")
put("author", "Dan Brown")
put("pages", 510)
put("prices", 19.95)
}
這樣還不夠,利用mapOf函數的寫法,使用”Apple” to 1語法結構快速創建一個鍵值對。在Kotlin中可以使用A to B的語法結構創建一個Pair對象。創建ContentValues.kt文件。
package com.example.myapplication
import android.content.ContentValues
//cvOf及接受一個Pair參數,在參數前面使用了vararg關鍵字,即允許0個、1個或者多個Pair類型的參數。這些參數會被傳入然後通過for-in循環
//fun cvOf(vararg pairs: Pair<String, Any?>): ContentValues {
// //Pair類型實踐支隊,ContentValues的鍵是String類型,將Pair鍵的泛型指定爲String,值可以爲任意類型,Any?允許傳入空值、字符串類等等類型
// val cv = ContentValues()
// //遍歷Pairs參數列表,取出其中的值放在ContentValues中。利用when判斷並覆蓋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
//}
//利用高階函數再行簡化
fun cvOf(vararg pairs: Pair<String, Any?>) = 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)
}
}
}
調用的函數如下:
val values2 = cvOf("name" to "The Lost Symbol","author" to "Dan Brown", "pages" to 510,"prices" to 19.95)
db.insert("Book", null, values2)
另外,更簡化的代碼如下,我們在build.gradle中導入KTX包,然後簡單調用即可:
implementation 'androidx.core:core-ktx:1.1.0'
val values2 = contentValuesOf(
"name" to "The Lost Symbol",
"author" to "Dan Brown",
"pages" to 510,
"prices" to 19.95
)
db.insert("Book", null, values2)