一个好的开发平台要对数据的存储有良好的支持,而不是仅靠保存在内存中的瞬时数据。本篇主要介绍Android平台实现数据存储的三种方式,分别是:
- File 文件存储
- SharedPreferences存储
- SQLite数据库存储
在分别介绍之前,先要知道以上三种方法使得数据文件都默认存储在哪里,方便后续验证,如图:
另外,关于如何在DDMS里File Explore下打开data文件夹以及导出文件在技能篇有详细说明,本篇不再赘述,验证时将直接展示导出文件内容。
1.File文件存储
在Android中写入和读取文件的方法,和 Java中实现I/O的程序是一样的,Context类中提供了openFileInput()和openFileOutput()方法来打开数据文件里的文件IO流。下面直接通过一个demo学习Android如何通过文件来保存数据。
(1)写入数据
新建布局mylayout.xml,只加入一个EditText用于输入文本内容:
新建MyActivity,在onCreate()获取EditText实例,然后重写onDestory()为了在活动销毁之前将输入的文本内容存储起来。具体方法是:先获取了EditText中输入的文本text,然后openFileOutput()方法将text存储到名为data的文件中并且能够得到一个FileOutputStream 对象,,再借助它构建出一 个OutputStreamWriter对象,接着再使用OutputStreamWriter 构建出一个 BufferedWriter 对象,这样就可以通过BufferedWriter来将文本内容写入到文件中了。
关键方法:
FileOutputStream fileOutputStream = openFileOutput("data", Context.MODE_PRIVATE);
bufferedWriter = new BufferedWriter(new OutputStreamWriter(fileOutputStream));
bufferedWriter.write(text);
其中openFileOutput()的第二个参数,表示文件的操作模式,常用的可选值含义见下图:
运行程序,在输入框里输入Hello AS!然后退出程序。
查看DDMS,果然有data文件!导出后内容和输入完全一致,证实了内容确实成功保存到文件了。
(2)读取数据
接下来我们想让程序再次启动时输入框内已经显示刚刚写入的数据。在OnCreate()中用openFileInput()方法指定了要从文件data中读取数据,之后代码和写入是对应的非常好理解。读取到内容之后判断是否为空,若不为空,就set到EditText里,并调用setSelection 方法将输入光标移动到文本的末尾位置以便于继续输入,再弹出一句重新加载成功的提示。
关键方法:
FileInputStream fileInputStream=openFileInput("data");
bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));
String line;
while((line=bufferedReader.readLine())!=null){
content.append(line);
}
运行程序,效果如图:
2.SharePreferences存储
SharePreferences是一种轻型的数据存储方式,常用来存储一些简单的配置信息,如int、string、boolean、float和long。它的本质是基于XML文件存储key-value键值对数据。
实现SharedPreferences存储的步骤如下:
(1)调用getSharedPreferences()方法获得SharedPreferences对象,提供两个参数,指定文件名和操作模式。
(2)调用SharedPreference对象的edit()方法获得SharedPreferences.Editor引用对象
(3)调用Edit接口的形如put某某某()方法以键值对形式保存某某某类型的数据
(4)调用Edit接口的commit()方法提交键值对。
一定注意,不用SharedPreferences对象去存储或修改数据,而是通过Editor对象。但获取数据时需要调用SharedPreferences对象的get某某某()方法了。
学习完SharePreferences基本知识点后,通过一个有记住密码功能的demo来巩固一下吧!新建login.xml,这里做一个登陆界面,如下图所示。相信这样的一个页面难不倒你,布局嵌套就能实现了。
在MainActvity的onCreate()里先获取除了TextView的所有控件、实例化SharedPreferences和SharedPreferences.Editor、给两个按钮都注册击事件,代码如下:
再来看看点击事件,当点击cancel时,直接finish()结束当前活动;当点击login时,第一轮判断用户名和密码是否对应(假设只有一个账户),如果不对应就弹出提示登陆不成功,如果对应就提示登陆成功并跳转到另一个页面,在跳转之前要进行第二轮判断以保证数据不丢失,判断CheckBox是否选中,如果未选中就用Edit清除所有数据,如果选中就添加用户名和密码,注意最后一定要commit提交。
下两个图就是跳转页面的布局和活动了。
最后再回到MainActivity的onCreate()方法中,下面这段代码的含义是:当用户选中复选框并且成功登陆一次之后,那么MyPres文件里肯定有数据了,这时如果重启登陆界面,SharedPreferences就会获取文件中数据并且呈现在输入框里了;当没有数据说明第一次打开界面或者用户不需要记住密码功能就什么也不需要显示了。
运行程序,测试一下:
并且记住密码功能也能很好的实现,这里就不展示了。如果用户选中复选框那么MyPres文件里就有数据了,见下图:
3.SQLite数据库存储
SQLite是一款轻量级的关系型数据库,它的运算速度非常快,占用资源很少,在存储大量复杂的关系型数据的时可以使用,比前面学过的只适用于存储简单数据的两种存储方式要好很多。接下来学习如何创建、升级数据库以及对数据进行增删改查,并穿插一个完整的例子更好的掌握这些知识点。
(1)创建数据库
先学习一个类SQLiteOpenHelper,它是SQLiteDatabase的帮助类, 用于管理数据库的创建和升级。SQLiteOpenHelper的使用步骤:
第一步:自定义帮助类并继承SQLiteOpenHelper,并重写两个方法:onCreate()和 onUpgrade(),分别在这两个方法中去实现创建、升级数据库的逻辑。还需要一个构造方法,这里用含有四个参数的构造方法就可以,如图:
第二步:创建数据库时,先实例化一个自定义的帮助类,并提供四个参数,含义是(上下文,数据库名,创建Cursor的工厂类,版本号)。
第三步:用帮助类对象的getReadableDatabase() 和getWritableDatabase()去创建或打开一个现有的数据库(如果数据库已存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库SQLiteDatabase。
第四步:之后就可以利用得到的数据库进行增删改查的操作了。
下面就来做个demo吧!从自定义帮助类开始,并重写两个方法及构造函数。在这里用帮助类帮助创建一个student表,包含学生的学号、姓名、年龄和年级,对应的SQL语句就放在一个字符串常量里。特别要注意语句一定要准确,多个空格都会建表失败。在onCreate()方法里会返回一个SQLiteDatabase对象,接下来终于接触到SQLiteDatabase的第一个常用方法execSQL(),这个方法非常万能,它可接受和处理SQL语句,换句话说,后面将要学习的增删改查不仅可以用提供好的现成的辅助性方法,还可直接用原生SQL语句再调用execSQL()就够了,在后面的学习中只介绍前一种方法。这里就调用execSQL()去创建表并打印一行提示的Toast。一个自定义帮助类构建好了!
然后新建布局,并放入五个按钮,对应创建和增删改查:
在主活动里获取所有按钮实例并注册点击事件,在onCreate()实例化帮助类MyHelper,指定数据库名为student.db,版本号为1。ContentValues类先跳过。
接下来实现点击创建按钮的效果:调用getReadableDatabase()创建数据库,且MyHelper的onCreate()也会执行,那么student表也被建立了。
运行程序,点击创建有Toast提示!但这不代表student表建立成功。所以到DDMS下找文件,确实有student.db数据库,但想要查看它需要别的工具,所以换一种查看方式,用adb shell来检查,命令如下:
可以证实student表成功建立了!
(2)增加数据
现在学习之前看到的ContentValues类,常用它put()方法以键值对的形式存储基本类型数据。在增和改会用到它,可以理解为键就是表中属性名,值就是表中数据。还常用方法clear()清空所有数据。
再来学习SQLiteDatabase用于增添数据的辅助性方法insert(),三个参数含义(被操作的表名,空值字段的名称,数据即ContentValues对象),第二个参数一般传入null。学会之后给student表插入两行记录吧,因为id这个属性设置了自增长所以可以不用管它:
运行程序,数据确实插入成功!
(3)删除数据
删除数据的辅助性方法是delete(),第一个参数还是表示表名,第二第三个参数用于约束删除某一行或几行的数据。比如需要删除student表中年龄大于17的记录:
运行,发现表中第二条记录果然被删除了!
(4)更改数据
update() 方法提供四个参数,(表名,ContentValues对象,约束,约束),之前都学过了!来试试给表里唯一的学生Lucy的年级更改为高三:
更改成功!
(5)查询数据
查询方法quary()复杂一些,需要至少七个参数(table, columns, selection, selectionArgs, groupBy, having, orderBy),含义是:(表名,要查询出的列名,查询条件子句,对应于selection语句中占位符的值,要分组的列名,分组后过滤条件,排序方式)。其实也不用害怕,多数情况下少数几个参数就能完成查询操作了。
还没完,这个方法会返回一个Cursor,查询到的数据都会从它取出。Cursor常用方法:moveToFirst()将指针移动到结果集的第一行;getColumnIndex()获取某一列在表中对应位置的索引;get某某某()传入索引以获取相应位置的某种类型的数据;close()关闭指针。下面来查询student表中所有数据:
打印出的日志结果如图:
(6)升级数据库
MyHelper里需要重写的第二个方法onUpdate()用于帮助数据库进行版本更新。比如此刻需要在数据库再添加一张表course,只要在update()写好创建course的操作,然后想办法让它被调用就好了。还记得在实例化帮助类是需要传入的第四个参数版本号吗?之前的传入的是1,只要传入一个比1大的数就可以让update()执行了。在下图代码里利用了oldVersion去判断旧版本号,如果是1就只需要再建course表,如果初次运行程序,就只会执行onCreate()方法然后两张表就一起建立了。这样做的好处是无论更新到第几代都不会影响之前的操作数据,也能保证当前版本是最新的。
修改版本号为2:
重新运行程序,点击创建,student表不会再创建而只会创建course表!
补充阅读:Litepal是一个轻型的数据库操作框架,采用了对象关系映射的模式,并对常用的数据库操作进行了封装,使用它会方便很多。感兴趣可见:项目主页,使用方法.
数据持久化技术就学习到这里~
> 下一篇预告:组件篇之ContentProvider