第7章:数据存储全方案,详解持久化技术


7.1 持久化技术简介

	android主要提供了以下3种方式用于数据持久化功能:
	
	1, 文件存储
	
	2, SharedPreferences存储
	
	3, 数据库存储
	
 
7.2 文件存储

7.2.1 将数据存储到文件中

	第一步:在主Activity的layout中添加一个控件EditText.
	
	第二步:在主Activity中修改如下代码。
    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)  # 二个模式,替换:MODE_PRIVATE, 追加:MODE_APPEND
            val writer = BufferedWriter(OutputStreamWriter(output))
            writer.use {
                it.write(inputText)
            }
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }
	
	第三步:使用Device File Explorer查看文件,路径如下:/data/data/com.example.[projectname]/file/
	
	
7.2.2 从文件中读取数据

	第一步:在主Activity中修改如下代码。
		# onCreate()中
	    val inputText = load()
        if (inputText.isNotEmpty()) {
            editText.setText(inputText)
            editText.setSelection(inputText.length)
            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 {
						content.append(it)
					}
				}
			} catch (e: IOException) {
				e.printStackTrace()
			}
			return content.toString()
		}

		
		
	第二步:测试代码,打开关闭应用测试
	

7.3 SharedPreferences存储

7.3.1  将数据存储到SharedPreferences中

	1, Context类中的getSharedPreferences()方法(默认MODE_PRIVATE)	 
		# 查看路径在/data/data/com.example.[projectname]/shared_prefs/中
		saveButton.setOnClickListener {
			val editor = getSharedPreferences("data", Context.MODE_PRIVATE).edit()
			editor.putString("name", "Tom")
			editor.putInt("age", 28)
			editor.putBoolean("married", false)
			editor.apply()
		}	

	
	2,Activity类中的getPreferences()方法
		(与getSharedPreferences相似,不过,将当前Activity类名作为存储文件名字,如下所示)
		saveButton.setOnClickListener {
			val editor = getPreferences(Context.MODE_PRIVATE).edit()
			editor.putString("name", "Tom")
			editor.putInt("age", 28)
			editor.putBoolean("married", false)
			editor.apply()
		}	

	
7.3.2 从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("MainActivity", "name is $name")
            Log.d("MainActivity", "age is $age")
            Log.d("MainActivity", "married is $married")
        }


7.3.3 实现记住密码功能

	第一步:创建一个登录界面。
	
	第二步:在密码和账号的编辑框中,实现登录效果,如果成功,就将数据保存。
	
	第三步:重启应用,如果有数据,就将数据恢复到编辑框中。
	
	
7.4 SQLite数据库存储

7.4.1 创建数据库

	SQLiteOpenHlper()(抽象类)
	有二个抽象方法:onCreate(),onUpgrade()
	
	有二个实例方法:共同点,是返回一个对数据库进行读写操作的对象。
	getReadableDatabase():如果满磁盘,返回的对象,只能只读。
	getWritableDatabase():如查满磁盘,出现异常。
	
	第一步:创建一个MyDatabaseHelper类。
	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," +
            "author text," +
            "price real," +
            "pages integer," +
            "name text," +
            "category_id integer)"
	override fun onCreate(db: SQLiteDatabase) {
        db.execSQL(createBook)
        Toast.makeText(context, "Create succeeded", Toast.LENGTH_SHORT).show()
    }
	
	override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {}
	
	}
	
	第二步:在主Activityr的onCreate()中添加如下代码。(布局中,添加一个按钮)
	    val dbHelper = MyDatabaseHelper(this, "BookStore.db", 1)	第三个参数为版本号
	    createDatabase.setOnClickListener {
            dbHelper.writableDatabase
        }
		
	
	第三步:启动应用,点击创建,查看db文件,路径如下:/data/data/com.example.[appname]/databases/目录下。
			右击这个db文件,Save as,保存到目标目录。
			
	第四步:安装Database Navigator,在file -> setings -> flugins -> maketplace中搜索安装重启android studio。
	
	第五步:在这个 Database Navigator plugins中导入刚刚的db文件。步骤如下:
			点击“加号”,在点击Database file中“加号”,选择db文件的路径,点击Ok,
			在connection -> Schemas -> main -> Book -> columns中查看列名。
			
	
7.4.2 升级数据库

	第一步:修改MyDatabaseHelper类。
	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," +
            "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)
        Toast.makeText(context, "Create succeeded", Toast.LENGTH_SHORT).show()
    }
	
	override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {}
		db.execSQL("drop table if exists Book")
		db.execSQL("drop table if exists Category")
		create(db)
	}
	
	第二步:在主Activityr的onCreate()中修改如下代码。(通过版本号的大小,更新数据库,执行onUpgrade())
	val dbHelper = MyDatabaseHelper(this, "BookStore.db", 2)	第三个参数为版本号
	createDatabase.setOnClickListener {
		dbHelper.writableDatabase
	}
	
	第三步:运行应用,点击按钮,即可更新表。


7.4.3 添加数据

	addData.setOnClickListener {
		val db = dbHelper.writableDatabase
		val values1 = ContentValues().apply {
			// 开始组装第一条数据
			put("name", "The Da Vinci Code")
			put("author", "Dan Brown")
			put("pages", 454)
			put("price", 16.96)
		}
		db.insert("Book", null, values1) // 插入第一条数据
		val values2 = ContentValues().apply {
			// 开始组装第二条数据
			put("name", "The Lost Symbol")
			put("author", "Dan Brown")
			put("pages", 510)
			put("price", 19.95)
		}
		db.insert("Book", null, values2) // 插入第二条数据,第二参数是默认值。
	}
	
7.4.4 更新数据
	
	    updateData.setOnClickListener {
            val db = dbHelper.writableDatabase
            val values = ContentValues()
            values.put("price", 10.99)
            val rows = db.update("Book", values, "name = ?", arrayOf("The Da Vinci Code"))
			// 第三、四个参数表示更新的条件,第四个是Kotlin语言的数组创建方法
            Toast.makeText(this, "rows is $rows", Toast.LENGTH_SHORT).show()
        }
		
7.4.5 删除数据

		deleteData.setOnClickListener {
            val db = dbHelper.writableDatabase
            db.delete("Book", "pages > ?", arrayOf("500")) //同上
        }
		
7.5.6 查询数据

		queryData.setOnClickListener {
            val db = dbHelper.writableDatabase
            // 查询Book表中所有的数据
			// db.query() 参数详解:
            val cursor = db.query("Book", null, null, null, null, null, null)
            if (cursor.moveToFirst()) {
                do {
                    // 遍历Cursor对象,取出数据并打印
                    val name = cursor.getString(cursor.getColumnIndex("name"))
                    val author = cursor.getString(cursor.getColumnIndex("author"))
                    val pages = cursor.getInt(cursor.getColumnIndex("pages"))
                    val price = cursor.getDouble(cursor.getColumnIndex("price"))
                    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 price is $price")
                } while (cursor.moveToNext())
            }
            cursor.close()
        }

7.4.7 使用SQL操作数据库

	// 通过DBHelper类获取一个读写的SQLiteDatabase对象
	SQLiteDatabase db = dbHelper.getWritableDatabase()
	// insert
	db.execSQL("insert into user (id,name,age) values (?,?,?)",arrayOf(1,"张三",18))
	// update
	db.execSQL("update user set name=? where id=?",arrayOf("张三",1))
	// delete
	db.execSQL("delete from user where id=1")
	// query
	Cursor cursor = db.rawQuery("select * from user where id=?", arrayOf("1"))
	
	
	
7.5 SQLite数据库最佳实践

7.5.1 使用事务

	# 创建一个
	fun cvOf(vararg pairs: Pair) = 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)
			}
		}
	}

	    replaceData.setOnClickListener {
            val db = dbHelper.writableDatabase
            db.beginTransaction() // 开启事务
            try {
                db.delete("Book", null, null)
                if (true) {
                    // 在这里手动抛出一个异常,让事务失败
                    throw NullPointerException()
                }
                val values = cvOf("name" to "Game of Thrones", "author" to "George Martin", "pages" to 720, "price" to 20.85)
                db.insert("Book", null, values)
                db.setTransactionSuccessful() // 事务已经执行成功
            } catch (e: Exception) {
                e.printStackTrace()
            } finally {
                db.endTransaction() // 结束事务
            }
        }

7.5.2 升级数据库的最佳写法
	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," +
				"author text," +
				"price real," +
				"pages integer," +
				"name text," +
				"category_id integer)"	//第2个版本号加的

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

		override fun onCreate(db: SQLiteDatabase) {  //第1个版本号加的
			db.execSQL(createBook)
			db.execSQL(createCategory)
			Toast.makeText(context, "Create succeeded", Toast.LENGTH_SHORT).show()
		}
		
		# 加版本判断的好处:当第0版,升级到第2版,会执行1,2个判断。当第1版升级到第2版,会执行第2个判断,版本都能正确的升级。
		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")
			}
		}
	}


	
	

		
		
		
		
	
	
	
	
	
	
		
		
	
	

你可能感兴趣的:(Kotlin技术技巧,android,android,studio,kotlin)