第七章 数据存储

160 阅读4分钟

第七章 数据存储

  1. 文件存储

    1. 将数据存储到文件中

      fun save(view: View) {
          try {
            	// 保存到/data/data/<package_name>/files/data文件中
              val openFileOutput = openFileOutput("data", Context.MODE_PRIVATE)
              val bufferedWriter = BufferedWriter(OutputStreamWriter(openFileOutput))
              bufferedWriter.use {
                  it.write(fileContentEdit.text.toString())
              }
          } catch (e: Exception) {
      
          }
      }
      

      Context.MODE_PRIVATE写入的内容将覆盖原内容

      Context.MODE_APPEND追加内容

      use()函数保证Lambda表达式中代码全部执行完之后自动将外层流关闭

    2. 从文件中获取内容

      fun read(view: View) {
          try {
              val openFileInput = openFileInput("data")
              val bufferedReader = BufferedReader(InputStreamReader(openFileInput))
              bufferedReader.use {
                  it.forEachLine {
                      fileContentEdit.setText(it)
                  }
              }
          } catch (e:Exception) {
          }
      }
      
  2. SharedPreferences

    1. 保存内容

      fun save(view: View) {
          // 保存到/data/data/<package_name>/shared_prefs/data.xml文件中
          val sharedPreferences = getSharedPreferences("data", Context.MODE_PRIVATE)
          val edit = sharedPreferences.edit()
          edit.putString("text", fileContentEdit.text.toString())
          edit.apply()
      }
      
    2. 获取内容

      fun read(view: View) {
          val sharedPreferences = getSharedPreferences("data", Context.MODE_PRIVATE)
          val string = sharedPreferences.getString("text", "")
          fileContentEdit.setText(string)
      }
      
  3. SQLite数据库存储

    1. 创建数据库

      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)"
      
          override fun onCreate(db: SQLiteDatabase) {
              db.execSQL(createBook)
              Toast.makeText(context, "Create success", Toast.LENGTH_SHORT).show()
          }
      
          override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
              TODO("Not yet implemented")
          }
      }
      
      class DatabaseActivity : BaseActivity() {
          private lateinit var myDatabaseHelper: MyDatabaseHelper
          override fun onCreate(savedInstanceState: Bundle?) {
              super.onCreate(savedInstanceState)
              setContentView(R.layout.activity_database)
              myDatabaseHelper = MyDatabaseHelper(this, "Bookstore.db", 1)
          }
      
          fun create(view: View) {
              myDatabaseHelper.writableDatabase
          }
      }
      
    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)"
      
          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 success", Toast.LENGTH_SHORT).show()
          }
      
          override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
              TODO("Not yet implemented")
              db.execSQL("drop table if exists Book")
              db.execSQL("drop table if exists Category")
              onCreate(db)
          }
      }
      
      class DatabaseActivity : BaseActivity() {
          private lateinit var myDatabaseHelper: MyDatabaseHelper
          override fun onCreate(savedInstanceState: Bundle?) {
              super.onCreate(savedInstanceState)
              setContentView(R.layout.activity_database)
              // 此处升级数据库版本号
              myDatabaseHelper = MyDatabaseHelper(this, "Bookstore.db", 2)
          }
      
          fun create(view: View) {
              myDatabaseHelper.writableDatabase
          }
      
          fun upgrade(view: View) {
              myDatabaseHelper.writableDatabase
          }
      }
      
    3. fun insert(view: View) {    val writableDatabase = myDatabaseHelper.writableDatabase    val va = ContentValues().apply {        put("name", "The Da Vinci Code")        put("author", "Dan Brown")        put("pages", 454)        put("price", 18.8)    }    writableDatabase.insert("Book", null, va)    val apply = ContentValues().apply {        put("name", "The Lost Symbol")        put("author", "Dan Brown")        put("pages", 510)        put("price", 50)    }    writableDatabase.insert("Book", null, apply)		// SQL语句    writableDatabase.execSQL("insert into Book(name, author, pages, price) values(?, ?, ?, ?)", arrayOf("Tha Da Vinci Code", "Dan Brown", "454", "15.55"))}
      
    4. fun update(view: View) {
          val apply = ContentValues().apply {
              put("price", 0.9)
          }
          myDatabaseHelper.writableDatabase.update(
              "Book",
              apply,
              "name = ?",
              arrayOf("The Da Vinci Code")
          )
      
          myDatabaseHelper.writableDatabase.execSQL("update Book set price = ? where name = ?", arrayOf(1.1, "The Lost Symbol"))
      }
      
    5. fun delete(view: View) {
          myDatabaseHelper.writableDatabase.delete("Book", "pages > ?", arrayOf("500"))
      
          myDatabaseHelper.writableDatabase.execSQL("delete from Book where name = ?", arrayOf("The Da Vinci Code"))
      }
      
    6. fun query(view: View) {
          val query = myDatabaseHelper.writableDatabase.query("Book", null, null, null, null,
              null, null, null )
          if (query.moveToFirst()) {
              do {
                  Toast.makeText(this,
                      "Book name is ${query.getString(query.getColumnIndex("name"))}, author is ${
                          query.getString(query.getColumnIndex("author"))}",
                      Toast.LENGTH_SHORT).show()
              } while (query.moveToNext())
          }
          query.close()
      
          val cursor = myDatabaseHelper.writableDatabase.rawQuery("select * from Book", null)
          cursor.close()
      }
      
  4. 使用事务

    在操作之前先开启事务,在数据库操作执行完成之后设置标记,最后结束事务

    fun transaction() {
        val db = myDatabaseHelper.writableDatabase
        db.beginTransaction()
        try {
            db.delete("Book", null, null)
    
            val apply = ContentValues().apply {
                put("price", 0.9)
            }
            db.update("Book", apply, "name = ?", arrayOf("The Da Vinci Code"))
            db.setTransactionSuccessful()
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            db.endTransaction()
        }
    }
    
  5. 升级数据库最佳写法

    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)"
        override fun onCreate(db: SQLiteDatabase) {
            db.execSQL(createBook)
        }
    
        override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        }
    }
    

    升级第二版:

    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)"
    
        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)
        }
    
        override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
            if (oldVersion <= 1) {
                db.execSQL(createCategory)
            }
        }
    }
    

    升级第三版

    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,  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)
        }
    
        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")
            }
        }
    }
    
  6. Kotlin课堂:高阶函数应用

    1. 简化SharedPreferences的用法

      fun SharedPreferences.open(block: SharedPreferences.Editor.() -> Unit) {
          val edit = edit()
          edit.block()
          // 或者这样 block(edit)
          edit.apply()
      }
      

      转化Java的实现,这里直接new Activity了,可以放到Activity中

      public class SharedPreferencesUtil {
      
          public static void main(String[] args) {
              SharedPreferences sharedPreferences = new Activity().getSharedPreferences("", 0);
              open(sharedPreferences, new Function1<SharedPreferences.Editor, Void>() {
                  @Override
                  public Void invoke(SharedPreferences.Editor editor) {
                      // 这里添加具体业务逻辑
                      editor.putInt("", 1);
                      return null;
                  }
              });
          }
      
          public static void open(SharedPreferences sp, Function1<SharedPreferences.Editor, Void> function1) {
              SharedPreferences.Editor edit = sp.edit();
              function1.invoke(edit);
          }
      }
      

      Kotlin中open函数接收的是一个SharedPreferences.Editor的函数类型参数,具体是什么函数不清楚。所以我们可以通过Editor的对象调用block()函数。

      除了内联函数中引用的Lambda,Kotlin中一切Lambda本质都是对象(Java实现)。

      优化前:

      val sharedPreferences = getSharedPreferences("data", Context.MODE_PRIVATE)
      val edit = sharedPreferences.edit()
      edit.putString("text", fileContentEdit.text.toString())
      edit.apply()
      

      优化后:

      getSharedPreferences("data", Context.MODE_PRIVATE).open {
          edit.putString("text", fileContentEdit.text.toString())
      }
      
    2. 简化ContentValues用法

      优化前:

      ContentValues().apply {
          put("name", "The Lost Symbol")
          put("author", "Dan Brown")
          put("pages", 510)
          put("price", 50)
      }
      

      先回顾下之前创建map:

      mapOf("1" to "One", "2" to "Two")
      
      fun ContentValues.cvOf(vararg pairs:Pair<String, Any?>) :ContentValues {
          val cv = ContentValues()
          for (pair in pairs) {
              val key = pair.first
              val value = pair.second
              when (value) {
                  null -> putNull(key)
                  is String -> put(key, value)
                  is Int -> put(key, value)
                  is Long -> put(key, value)
                  is Boolean -> put(key, value)
                  is Float -> put(key, value)
                  is Double -> put(key, value)
                  is ByteArray -> put(key, value)
                  is Byte -> put(key, value)
                  is Short -> put(key, value)
                  else -> {
                      val valueType = value.javaClass.canonicalName
                      throw IllegalArgumentException("Illegal value type $valueType for key \"$key\"")
                  }
              }
          }
          return cv
      }
      

      vararg可变参数列表

      Any?泛型,Any是Kotlin中所有类的共同基类,而Any?表示可以传入空值

      使用apply后的优化效果

      // 此处传入pair.size在创建ContentValues时就指定了大小,避免扩容时的性能损耗
      fun contentValuesOf(vararg pairs: Pair<String, Any?>) = ContentValues(pairs.size).apply {
          for ((key, value) in pairs) {
              when (value) {
                  null -> putNull(key)
                  is String -> put(key, value)
                  is Int -> put(key, value)
                  is Long -> put(key, value)
                  is Boolean -> put(key, value)
                  is Float -> put(key, value)
                  is Double -> put(key, value)
                  is ByteArray -> put(key, value)
                  is Byte -> put(key, value)
                  is Short -> put(key, value)
                  else -> {
                      val valueType = value.javaClass.canonicalName
                      throw IllegalArgumentException("Illegal value type $valueType for key \"$key\"")
                  }
              }
          }
      }
      

      优化前:

      val apply = ContentValues().apply {
          put("name", "The Lost Symbol")
          put("author", "Dan Brown")
          put("pages", 510)
          put("price", 50)
      }
      

      优化后:

      val book = cvOf(
          "name" to "The Lost Symbol",
          "author" to "Dan Brown",
          "pages" to 510,
          "price" to 50
      )