The first line of code (the third version) Chapter 7 persistent storage

Chapter 7 data persistence

7.2 document storage

7.2.1 storing data in files

Store simple text data or binary data

  1. Put an EditText

  2. code

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    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)//Context.MODE_PRIVATE overwrites the contents of the original file or creates a new file, context MODE_ Append if there is an existing file, append the content
            val writer = BufferedWriter(OutputStreamWriter(output))
            writer.use { //Built in extension function, which automatically closes the outer layer flow after all the code in Lambda expression is executed
                it.write(inputText)
            }
        }catch (e:IOException){
            e.printStackTrace()
        }
    }
}
  1. In / data/data / package name / directory

7.2.2 reading data from files

val inputText = load()
if (inputText.isNotEmpty()) {
    editText.setText(inputText)
    editText.setSelection(inputText.length)//Sets the position of the cursor
    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 { //Built in expansion function, callback each row of data to Lambda expression
                    content.append(it)
                }
            }
        }catch (e:IOException){
            e.printStackTrace()
        }
        return content.toString()
    }

7.3 SharedPreferences storage

7.3.1 store data in SharedPreferences

  • Context getSharePreferences
  • Activitity getPreferences
saveButton.setOnClickListener { 
    val editor = getSharedPreferences("data",Context.MODE_PRIVATE).edit()
    editor.putString("name", "Tom")
    editor.putInt("age",28)
    editor.putBoolean("married", false)
    editor.apply()
}

7.3.2 reading data from 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(TAG, "name is $name")
    Log.d(TAG, "age is $age")
    Log.d(TAG, "married is $married")
}

7.3.3 realize the function of remembering passwords

Based on broadcast best practices

  1. Add check box
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    >
    <CheckBox
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/rememberPass"></CheckBox>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:text="Remember password"
        ></TextView>
</LinearLayout>
  1. code
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","")
    accountEdit.setText(account)
    passwordEdit.setText(password)
    rememberPass.isChecked = true
}
login.setOnClickListener {
    val account = accountEdit.text.toString()
    val password = passwordEdit.text.toString()
    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{
            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()
    }
}

7.4 SQLite database storage

7.4.1 create database

  1. Implement SQLiteOpenHelper abstract class, override create and upgrade abstract methods, and use them to create and update databases respectively
class MyDatabaseHelper(val context: Context, name:String, version:Int) : SQLiteOpenHelper(context,name,null,version){
  //Table creation statement 
  private val createBook = "create table Book("+
            " id integer primary key autoincrement,"+//Set id as the primary key, self growing
            "author text,"+
            "price real,"+//float 
            "pages integer,"+
            "name text)"

    override fun onCreate(p0: SQLiteDatabase) {
        p0.execSQL(createBook)
        Toast.makeText(context,"Created succeeded",Toast.LENGTH_SHORT).show()
    }

    override fun onUpgrade(p0: SQLiteDatabase?, p1: Int, p2: Int) {
    }
}
  1. Write the code to create the database
val dbHelper = MyDatabaseHelper(this,"BookStore.db",1)//Database name and version number
createDatabase.setOnClickListener { dbHelper.writableDatabase//Create database}
  1. Use the plug-in to view the created database
  • First, find the file address of the database (save as export)
  • Download Database Navigator
  • Plus sign - SQLite - modify the file address to the place just exported

7.4.2 upgrading database

  1. code
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,"+//Set id as the primary key, self growing
            "author text,"+
            "price real,"+//float 
            "pages integer,"+
            "name text)"
		//Table creation statement
    private val createCategory = "create table Category ("+
            "id integer primary key autoincrement,"+
            "category_name text,"+
            "category_code integer)"

    override fun onCreate(p0: SQLiteDatabase) {
        p0.execSQL(createBook)
        p0.execSQL(createCategory)//A newly created database is required
        Toast.makeText(context,"Created succeeded",Toast.LENGTH_SHORT).show()
    }

    override fun onUpgrade(p0: SQLiteDatabase, p1: Int, p2: Int) {
    //Delete the previous table
        p0.execSQL("drop table if exists Book")
        p0.execSQL("drop table if exists Category")
        //Recall create statement
        onCreate(p0)
    }


}
  1. Upgrade version number
val dbHelper = MyDatabaseHelper(this,"BookStore.db",3)

7.4.3 adding data

addData.setOnClickListener {
		//Get database object
    val db = dbHelper.writableDatabase
    //Assign value to ContentValue
    val value1 = ContentValues().apply {
        put("name","The Da Vinci Code")
        put("author","Dan Brown")
        put("pages",454)
        put("price",16.96)
    }
    //insert data
    db.insert("Book",null,value1)

    val value2 = ContentValues().apply {
        put("name","The Lost Symbol")
        put("author","Dan Brown")
        put("pages",510)
        put("price",19.95)
    }
    db.insert("Book",null,value2)

}

View the added data: re store the database file, and then use the plug-in to retrieve it. Double click the table and click no filter

7.4.4 update data

updateData.setOnClickListener {
            val db = dbHelper.writableDatabase
            val values = ContentValues()
            values.put("price",10.99)
            db.update("Book",values,"name = ?", arrayOf("The Da Vinci Code"))//kotlin quick creation of arrays
        }
//In this table, the price of the book named The Da Vinci Code is changed to 10.99

Add: you can view the table data directly in App inspection without plug-ins

7.4.5 deleting data

deleteData.setOnClickListener {
    val db = dbHelper.writableDatabase
    db.delete("Book","pages > ?", arrayOf("500"))
}
//If the number of pages is more than 500, all will be deleted

7.4.6 query data

  • Query seven parameters
  1. table name

  2. Columns: which columns to query

  3. selection 4. selectionArgs constraint queries a certain row or several rows

  4. groupBy specifies the column that requires group by

  5. having further filtering after the above operation

  6. orderBy specifies the sort method. If not specified, the default sort will be used

  • Return value: Cursor
queryData.setOnClickListener {
    val db = dbHelper.writableDatabase
    //Query all data in the table
    val cursor = db.query("Book", 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.getString(cursor.getColumnIndex("pages"))
            val price = cursor.getString(cursor.getColumnIndex("price"))
            Log.d(TAG, "book name is $name")
            Log.d(TAG, "book author is $author")
            Log.d(TAG, "book pages is $pages")
            Log.d(TAG, "book price is $price")
        }while (cursor.moveToNext())

    }
    cursor.close()
}

7.5.1 usage transactions

Transaction: ensure that a series of operations are either fully executed or not executed

replaceData.setOnClickListener {
            val db = dbHelper.writableDatabase
            db.beginTransaction()
            try {
                db.delete("Book",null,null)
                if (true) {
                    //Manually throw an exception to make the transaction fail
//                    throw NullPointerException()
                    val values = ContentValues().apply {
                        put("name", "Game of Thrones")
                        put("author", "George Martin")
                        put("pages", 720)
                        put("price",20.85)
                    }
                    db.insert("Book", null, values)
                    db.setTransactionSuccessful()
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }finally {
                db.endTransaction()
            }
        }

7.5.2 best writing method for upgrading database

  • first edition
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,"+//Set id as the primary key, self growing
            "author text,"+
            "price real,"+//float 
            "pages integer,"+
            "name text)"

    override fun onCreate(p0: SQLiteDatabase) {
        p0.execSQL(createBook)
    }
    
    override fun onUpgrade(p0: SQLiteDatabase, p1: Int, p2: Int) {
    }

}
  • The Category table needs to be added in the second edition
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,"+//Set id as the primary key, self growing
            "author text,"+
            "price real,"+//float 
            "pages integer,"+
            "name text)"

    private val createCategory = "create table Category ("+
            "id integer primary key autoincrement,"+
            "category_name text,"+
            "category_code integer)"

    //If you download version 2 directly, you will create a table
    override fun onCreate(p0: SQLiteDatabase) {
        p0.execSQL(createBook)
        p0.execSQL(createCategory)
     
    }

    //If you overwrite version 1 with version 2, a new table will be created
    override fun onUpgrade(p0: SQLiteDatabase, oldVersion: Int, p2: Int) {
        if(oldVersion <=1){
            p0.execSQL(createCategory)
        }
    }
}
  • In the third edition, category needs to be added to the Book table_ ID field
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,"+//Set id as the primary key, self growing
            "author text,"+
            "price real,"+//float 
            "pages integer,"+
            "name text,"+
            "category_id integer)"

    private val createCategory = "create table Category ("+
            "id integer primary key autoincrement,"+
            "category_name text,"+
            "category_code integer)"

    //If you download version 2 directly, you will create a table
    override fun onCreate(p0: SQLiteDatabase) {
        p0.execSQL(createBook)
        p0.execSQL(createCategory)

    }

    //If you overwrite version 1 with version 2, a new table will be created
    override fun onUpgrade(p0: SQLiteDatabase, oldVersion: Int, p2: Int) {
        if(oldVersion <=1){
            //If the current version is the first version and the third version is updated, both will be executed
            p0.execSQL(createCategory)
        }
        if (oldVersion <= 2) {
            //If the current version is the second version and the third version is updated, this create column will be executed
            p0.execSQL("alter table Book add column category_id integer")
        }
    }

}

7.6 Kotlin class: application of higher order functions

7.6.1 simplify the use of SharedPreferences

Initial writing

val editor = getSharedPreferences("data",Context,MODE_PRIVATE).edit()
editor.putString("name","Tom")
editor.apply()
  1. Define the top-level method SharePreferences.kt
fun SharedPreferences.open(block: SharedPreferences.Editor.() -> Unit) {
    val editor = edit()
    editor.block()
    editor.apply()
}
  1. call
getSharedPreferences("data", Context.MODE_PRIVATE).open {
    putString("name","Tom")
}
  1. This implementation is already available in the KTX extension library
getSharedPreferences("data", Context.MODE_PRIVATE).edit {
    putString("name","Tom")
}

7.6.2 simplify the use of ContentValues

  • Initial writing
val values = ContentBalues()
values.put("name","Game of Thrones")
db.insert("Book",null,values)
  • The supplementary mapOf function allows A to B method to quickly create a key value Pair, and the return value is the Pair object
/**Pair Is the type produced by A to B
 * Any Is the base class of all classes, equivalent to Object in java
 * ´╝čThe value can be empty
 * vararg Modifier indicates that multiple Pair type parameters are allowed to be passed to the method
 */
fun cvOf(vararg pairs: Pair<String,Any?>):ContentValues {
    val cv = 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
}
  • call
val values = cvOf("name" to "Game of Thrones","author" to "George Martin","pages" to 720,"price" to 20.85)
db.insert("Book",null,values)
  • Further optimization
//The return value of apply is the calling object
//Single line code function syntax sugar, the equal sign replaces the declaration of the return value
fun cvOf(vararg pairs: Pair<String,Any?>)= ContentValues().apply {
    val cv = ContentValues()
    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)
        }
    }
}
  • Library provided by the system
val values = contentValuesOf("name" to "Game of Thrones","author" to "George Martin","pages" to 720,"price" to 20.85)
db.insert("Book",null,values)

Tags: Android Android Studio kotlin

Posted by Nicholas Reed on Mon, 29 Aug 2022 06:12:52 +0930