Chapter 7 data persistence
7.2 document storage
7.2.1 storing data in files
Store simple text data or binary data
-
Put an EditText
-
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() } } }
- 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
- 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>
- 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
- 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) { } }
- 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}
- 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
- 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) } }
- 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
-
table name
-
Columns: which columns to query
-
selection 4. selectionArgs constraint queries a certain row or several rows
-
groupBy specifies the column that requires group by
-
having further filtering after the above operation
-
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()
- Define the top-level method SharePreferences.kt
fun SharedPreferences.open(block: SharedPreferences.Editor.() -> Unit) { val editor = edit() editor.block() editor.apply() }
- call
getSharedPreferences("data", Context.MODE_PRIVATE).open { putString("name","Tom") }
- 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)