Android三种数据存储的方式
Posted z啵唧啵唧
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android三种数据存储的方式相关的知识,希望对你有一定的参考价值。
文章目录
android数据存储技术
- 在任何一个程序当中说白了就是和数据在不停的打交道
- 如果我们的应用程序没有数据那么就变成了一种空壳子,对用户来说没有实际的意义
- 并且我们想要对关键数据进行保存,就需要用到数据的持久化了
持久化技术
- 数据持久化就是指将那些内存当中的数据保存到设备当中,保证手机在关机的情况下这些数据仍然不会丢失.
- 保存在内存当中的数据是瞬时状态的,保存在设备当中的数据是持久状态的
- 持久化技术提供了一种机制,可以让数据在瞬时状态和持久状态之间进行转换
- 在Android系统当中主要提供了三种方式进行持久化:文件存储,SharePreferences存储以及数据库存储
文件存储
- 文件存储时Android中最基本的数据存储方式,他不对存储的内容进行任何格式化处理,所有的数据都是原封不动的保存在文件当中的,因为他比较适合存储一些简单的文本数据和二进制数据
- 如果需要使用文件存储的方式来保存一些较为复杂的数据结构,就需要定义一套自己的格式规范,方便之后将数据从文件当中重新解析出来.
将数据存储到文件当中
- 在Context类当中提供了一个openFileOutput()方法,用于将数据存储到文件当中该方法接收两个参数.
- 第一个是文件名,在文件创建的时候进行使用,注意这里指定的文件名不可以包含路径,因为所有的文件都默认存储到/data/data//files/目录下面
- 第二个参数是文件的操作模式,主要有MODE_PRIVATE和MODE_APPEND两种模式可以选择,默认是MODE_PRIVATE,表示当指定相同文件名的时候,所写入的内容就会覆盖原先的内容,而MODE_APPEND则代表文件如果存在的话,就往文件当中追加内容,不存在就新创建文件.
- 本来还有另外两种操作模式:MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE.这两种模式表示允许其他应用程序对我们程序中的文件进行读写操作,但是这两种模式过于危险,很容易引发安全漏洞,早在安卓4.2版本就放弃了.
- openFileOutput()方法的返回值是一个FileOutputStream对象,得到这个对象之后就可以使用Java流的方式将数据写入文件当中了.
- 以下是一段示例,表示如何将一段文本保存到文件当中
fun save(inputText: String)
try
val output = openFileOutput("data", Context.MODE_PRIVATE)
val writer = BufferWriter(OutputStreamWriter(output))
writer.use
it.write(inputText)
catch (e: IOException)
e.printStackTrace()
- 上述代码的主要意思就是先使用openFileOutput()方法得到一个FileOutputStream对象,然会借助这个FileOutputStream对象构建出一个OutputStreamWriter对象,然后再借助OutputStreamWriter对象构建出一个BufferWriter对象,然后使用use扩展方法,在该扩展方法中使用Lambda表达式调用write方法将文本写入到文件当中.
- 其中use方法是Kotlin内置的扩展函数,他会保证在Lambda表达式中的代码全部执行完毕之后自动将外层的流进行关闭这样我们就不需要再编写一个finally语句,手动去关闭流,这是一个非常好的扩展函数.
- 在Kotlin当中是没有异常检查机制的,这就意味着Kotlin编写的所有代码都不会强制要求你将异常进行捕捉和抛出.
示例_将数据存储到文件当中
- MainActivity代码
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)
val writer = BufferedWriter(OutputStreamWriter(output))
writer.use
it.write(inputText)
catch (e: IOException)
e.stackTrace
- 重写了onDestroy()方法,这样就可以保证在Activity销毁之前一定会调用这个方法.
- 在onDestroy()方法中,我们获取了EditText中输入的内容,并调用save()方法将输入的内容存储到文件当中,文件命名为data
- activity_main.xml代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/editText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="Type something here" />
</LinearLayout>
- 布局文件中就是给我们的程序提供了一个输入框,用于输入我们要保存的数据
示例_从文件当中读取数据
- 类似于将数据存储到文件当中,Context类中还提供了一个openFileInput,用于从文件当中读取数据,这个方法只需要接收一个参数就是文件名,然后自动会到/data/data//files/目录下面加载这个文件,并且返回一个FileInputStream对象,通过这个对象我们就可以将数据读取出来了
- 编写读取数据的load函数
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.stackTrace
return content.toString()
- 在onCreate()方法当中调用load函数实现我们启动MainActivity的时候,文本框能够保留我们上次输入的内容
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val inputText = load()
if (inputText.isNotEmpty())
//设置editText的内容为我们从文件中读取出来的内容
editText.setText(inputText)
//将光标移动到editText的末尾位置
editText.setSelection(inputText.length)
//弹出一个提示
Toast.makeText(this, "succeeded", Toast.LENGTH_SHORT).show()
SharedPreferences存储
- 不同于文件的存储方式,SharedPreferences是使用键值对的方式来存储数据的.
- 也就是说当保存当保存一条数据的时候,需要给这条数据提供一个对应的键,这样在读取数据的时候,就可以通过这个键将数据读取出来.
- SharedPreferences支持多种不同的数据类型存储,如果存储的数据类型是整形,那么读取出来的数据也是整形的
- 如果存储的数据是字符串,那么读取出来的数据也就是一个字符串.
- 所以整体来说,SharedPreferences存储数据的方式比文件存储的方式更加方便.
将数据存储到SharedPreferences
- 想要使用SharedPreferences存储数据,首先需要获取SharedPreferences对象,Android中主要提供了以下两种方式用于得到SharedPreferences对象
1.Context类中getSharedPreferences()方法
- 此方法接收两个参数
- 第一个参数:用于指定SharedPreferences文件的名称,如果指定的文件不存在则会创建一个,SharedPreferences文件都是存放在/data/data//shared_prefs/目录下
- 第二个参数:用于指定操作模式,目前只有默认的MODE_PRIVATE这一种模式可选,他和直接传入0的效果是相同的,表示只有当前程序才可以对这个SharedPreferences文件进行读写
- 其他的几种操作模式已经被废除
2.Activity类中的getSharedPreferences()方法
- 这个方法和Context中的方法非常相似,不过他只接收一个参数,就是操作模式
- 因为这个方法会自动将当前Activity的类名作为SharedPreferences的文件名
- 得到SharedPreferences对象之后就可以往SharedPreferences文件中存储数据了,主要分为三个步骤:
- 调用SharedPreferences对向的edit()方法获取一个SharedPreferences.Editor对象
- 向SharedPreferences.Editor对象中添加数据,比如添加一个布尔类型的对象就使用putBoolean()方法
- 调用apply()方法将添加的数据提交,从而完成数据存储操作
往SharedPreferences文件中存放数据示例
- 创建一个项目SharedPreferencesTest
- 编写activity_main.xml中的代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/saveButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Save Data" />
</LinearLayout>
- 在页面当中放置了一个按钮,用于将一些数据存储到SharedPreferences文件当中
- 然后修改MainActivity当中的代码
class MainActivity : AppCompatActivity()
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", "Tom")
editor.putInt("age", 18)
editor.putBoolean("married", false)
editor.apply()
- 在上述的代码当中,给按钮注册了一个点击事件,然后在点击事件当中通过getSharedPreferences()方法指定SharedPreferences的文件名为data,并得到SharedPreferences.Editor对象.
- 接着给这个对象当中提交了三条不同类型的数据
- 最后调用apply()方法进行提交,从而完成了数据存储的操作.
- 打开相应的路径看到数据已经保存在一个xml文件当中
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="name">Tom</string>
<boolean name="married" value="false" />
<int name="age" value="18" />
</map>
- 那么就看看如何从SharedPreferences文件当中读取数据
从SharedPreferences文件当中读取数据
- 根据上面的示例来说,使用SharedPreferences来存储数据是比较简单的,从SharedPreferences中读取数据也是非常简单的
- SharedPreferences对象中提供了一系列的get方法,用于读取存储的数据,每种get方法对应了SharedPreferences.Editor中的一种put方法,比如读取一个布尔类型的数据就是用getBoolean()方法,这些get方法都接受两个参数
- 第一个参数是键,传入存储数据的时候使用的键就可以得到相应相应的值
- 第二个参数是默认值,即表示当传入的键找不到对应的值的时候以什么样的默认值进行返回.
从SharedPreferences文件当中读取数据的例子
- 在activity_main.xml当中添加一个读取数据的按钮
- 修改MainActivity当中的代码
利用SharedPreferences实现一个记住密码的功能
- 修改之前登录界面的案例
- 在activity_login.xml文件当中添加一个复选控件
- 在上述代码当中添加了一个chechkBox复选框,用户可以通过点击这个复选框确定自己是否需要记住密码
- 修改LoginActivity当中的代码逻辑
- 根据记住密码的状态选择是否将用户的信息填充到输入框
//如果remember_password对应的是true说明用户之前记住了密码,就可以使用存储的密码来填充输入框
if (isRemember)
//将账号和密码都取出之后填充到输入框当中
val account = prefs.getString("account", "")
val password = prefs.getString("password", "")
accountEdit.setText(account)
passwordEdit.setText(password)
//将复选框设置为选中
rememberPass.isChecked = true
- 点击登录按钮,根据复选框当前被点击的状态判断是否保存当前用户输入的信息
login.setOnClickListener
//先从Edit当中获取账户名和密码
val account = accountEdit.text.toString()
val password = passwordEdit.text.toString()
//验证账户名和密码的逻辑
if (account == "admin" && password == "123456")
//如果账号和密码都没有问题,那么就跳转到MainActivity
// val intent = Intent(this, MainActivity::class.java)
// startActivity(intent)
// finish()
//先获取一个editor对象
val edit = prefs.edit()
//说明账号和密码都没有问题,现在我们要根据用户是否选择复选框来决定将用户的账号和密码添加到SharedPreferences进行保存
if (rememberPass.isChecked)
//如果复选框被选中,那么我们就将用户的信息进行保存
//保存账号
edit.putString("account", account)
//保存密码
edit.putString("password", password)
//保存是否基础密码这个状态
edit.putBoolean("remember_password", true)
//提交保存信息
edit.apply()
else
//说明用户这一次没有选择记住密码,那么我们调用clear()方法清空SharedPreferences文件当中的信息
edit.clear()
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
finish()
else
//使用Toast提示一段账号或者密码错误的文本
Toast.makeText(this, "account or password is invalid", Toast.LENGTH_SHORT).show()
SQLite数据库
- SQLite数据库是安卓系统内置的数据库,是一款轻量级的关系型数据库
- SQLite数据库运算速度非常快,占用资源很少
- 支持标准的SQL语法还支持数据库的ACID事务
创建数据库
- Android为了更加方便我们使用SQLite数据库,专门提供了一个SQLiteOpenHelper帮助类,借助这个类可以非常简单的对数据库进行创建和升级.
- SQLiteOpenHelper是一个抽象类,我们需要创建自己的帮助类去继承它,在这个类当中有两个抽象方法:onCreate()和onUpgrade().我们必须在自己写的子类中重写这两个方法,然后对数据库进行创建和升级
- 在SQLiteOpenHelper类当中还有非常重要的两个实例方法:getReadableDatabase()和getWriteableDatabase()
- 这两个方法都可以创建或者打开一个现有的数据(如果数据库以及存在则直接打开,否则创建一个新的数据库),并且返回一个可对数据库进行读写操作的对象.
- 不同的是,当数据库不可以写入的时候(比如磁盘已经满了),getReadDatabase()方法返回的对象将会以只读的方式打开数据库,而getWriteDatabase()则会抛出异常.
- SQLiteOpenHelper中有两个构造参数提供给我们进行选择,一般使用参数少一点的哪个构造参数即可,这个构造参数接收四个参数
- 第一个参数是:Context
- 第二个参数是数据库的名字,创建数据库的时候使用的就是这里指定的名字
- 第三个参数允许我们在查询数据的时候返回一个自定义的Cursor,一般传入null即可
- 第四个参数表示当前数据库的版本号,可以用来给数据库进行升级操作.
- 构建出SQLiteOpenHelper的实例之后就可以调用两个重要的实例方法就可以创建数据库了,数据库的文件会存放在/data/data//databases/目录下.此时重写的onCreate()方法也会得到执行,所以通常会在这里处理一些创建表的逻辑.
使用SQLite的示例
- 创建一个BookStore.db数据库,然后在这个数据库当中创建一个Book表
- 首先新建一个MyDatabaseHelper类继承SQLiteOpenHelper类,然后再该类中的onCreate()方法中创建一个Book表
class MyDatabaseHelper(val context: Context, val name: String, val version: Int) :
SQLiteOpenHelper(context, name, null, version)
//创建Book表的SQL语句
private val createBookSQL = "create table Book (" +
"id integer primary key autoincrement," +
"author text," +
"price integer," +
"pages integer," +
"name text)"
override fun onCreate(db: SQLiteDatabase)
//调用execSQL创建表
db.execSQL(createBookSQL)
Toast.makeText(context, "Create succeeded", Toast.LENGTH_SHORT).show()
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int)
TODO("Not yet implemented")
- 在activity_main.xml文件当中添加一个按钮用于创建数据库
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/createDatabase"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="create database"
android:textSize="18sp" />
</LinearLayout>
- 在MainActivity中编写该按钮的逻辑
class MainActivity : AppCompatActivity()
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//创建一个MyDatabaseHelper的实例
val myDatabaseHelper = MyDatabaseHelper(this, "BookStore.db", 1)
createDatabase.setOnClickListener
myDatabaseHelper.writableDatabase
- 在onCreate()方法当中构建了一个MyDatabaseHelper对象,并在该对象当中指定了数据库和版本号,然后在按钮当中调用了getWritableDatabase()方法
- 这样当第一次点击按钮的时候就会检查当前程序中有没有BookStore.db这个数据库,于是会创建好该数据库并且调用MyDatabaseHelper类中onCreate()方法来创建Book表.
- 然后我们在插件市场下载Database Navigator即可使用可视化的界面管理我们刚刚创建的数据库
升级数据库
- onUpgrade()方法是对于数据库进行升级的,他在整个数据库的管理工作当中起着非常重要的作用
- 之前我们给数据库创建了一张Book表,现在我们想给数据库再创建一张分类表Category,我们将这条语句添加到MyDatabaseHelper中代码如下:
- 看上去都是没有问题的,但是我们重新运行一下程序,并点击"Create Database"按钮,但是没有弹出创建成功的提示.通过DB Browser工具到数据库中再去检查一下,发现Category表还是没有创建成功.
- 创建不成功的原因主要是,BookStore.db数据库已经存在了,之后不管我们再怎样点击"Create Database"按钮,MyDatabaseHelper中的onCreate()方法都是不会再进行执行,因此新添加的表也就无法进行创建了.
- 解决这个问题的办法也比较简单,只需要先将程序进行卸载,然后再进行重新运行,这时候BookStore.db数据库就已经不存在了,此时再点击按钮就可以进行onCreate()方法当中逻辑的执行.
- 不过通过卸载一个程序来添加一张表比较不合理,这时候我们只需要巧妙运用onUpgrade()方法即可解决这个问题,修改MyDatabaseHelper中的代码
- 可以看到我们在onUpgrade()方法当中执行了两条drop语句,如果发现数据库中已经存在Book表和Category表,就将这两张表删除,然后调用onCreate()方法直接重新创建,这里先将已经存在的表删除了,是因为如果在创建表时发现这张表已经存在了,就会直接进行报错.
- 接下来我们就需要想办法触发onUpgrade()方法进行执行,这个就只需要我们在创建MyDatabaseHelper实例的时候将传入的版本号进行升级即可
- 之前创建数据库的时候传递的是1,现在只要传递一个比1大的数即可,表示我们想要对数据库进行升级
添加数据
- 使用SQLiteOpenHelper的getReadableDatabase()方法或者getWriteableDatabase()方法可以用于对数据库进行进行创建和升级,不仅如此这两个方法还会返回一个SQLiteDatabase对象,借助这个对象就可以对数据进行CRUD操作了.
- SQLiteDatabase中提供了一个inset()方法,专门用于添加数据,他接收三个参数.
- 第一个参数是表名
- 第二个参数用于在未指定添加数据的情况下給某些可以为空的列自动赋值为NULL,一般我们用不到这个功能,直接传入null即可.
- 第三个参数是ContentValues对象,它提供了一系列的put()方法重载,用于向ContentValues中添加数据,只需要将表中的每个列名以及相应的待添加数据传入即可.
- 在activity_main.xml中编写一个添加数据的按钮
- 在该按钮的点击事件当中添加逻辑
addData.setOnClickListener
val db = myDatabaseHelper.writableDatabase
val values1 = ContentValues().apply
//开始组装第一条数据
put("name", "The Da Vinci Code")
put("author", "Dan Brown")
put("pages", 454)
put("price", 19.67)
//插入第一条数据
db.insert("Book", null, values1)
val values2 = ContentValues()Android中常用的三种存储方法浅析