1.文件儲存
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.SharedPreferences
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文件內
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<EditText
android:id="@+id/accountid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="賬號"
></EditText>
<EditText
android:id="@+id/passwordid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="密碼"
></EditText>
<CheckBox
android:id="@+id/rememberPass"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></CheckBox>
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="登陸"></Button>
</LinearLayout>
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.SQLite數據庫儲存
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.Kotlin高階課堂
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<String , Any?>) : 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<String , Any?>) = 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)