Android开发7:简单的数据存储(使用SharedPreferences)和文件操作
前言
啦啦啦~大家好,又见面啦~
本篇博文讲和大家一起完成一个需要注册、登录的备忘录的,一起学习 SharedPreferences 的基本使用,学习 Android 中常见的文件操作方法,复习 Android 界面编程。
直接进入正题~
基础知识
1.SharedPreferences 的使用
使用SharedPreferences储存用户名和密码,SharedPreferences是直接处理xml文件,不需要做字符串分割,存储效率会比使用内部存储,和外部存储存储用户名和密码高。
(1) SharedPreferences 的读取
在 Android 中,用于获取 SharedPreferences 的接⼝是 getSharedPreferences(String, int) 函数。 两个参数的意义:
String: Desired preferences file. If a preferences file by this name does not exist, it will be created when you retrieve an editor.
int: Operating mode. Use 0 or MODE_PRIVATE for the default operation.
我们对 SharedPreferences 的读取操作是通过 getSharedPreferences(String, int) 函数返回的 SharedPreferences 对象的方法来完成的。查阅文档可以看到,SharedPreferences 支持如下几种方法读取之前存储的数据:
- abstract Map<String, ?> getAll()
- abstract boolean getBoolean(String key, boolean defValue)
- abstract float getFloat(String key, float defValue)
- abstract int getInt(String key, int defValue)
- abstract long getLong(String key, long defValue)
- abstract String getString(String key, String defValue)
- abstract Set<String> getStringSet(String key, Set<String> defValues)
所有方法都需要传入一个 defValue 参数,在给定的 key 不存在时,SharedPreferences 会直接返回 这个默认值。
(2)SharedPreferences 的写入
所有对 SharedPreferences 的写入操作,都必须通过 SharedPreferences.edit() 函数返回的 Editor对象来完成。
举例:
Context context = getActivity();
SharedPreferences sharedPref = context.getSharedPreferences("MY_PREFERENCE", Context.MODE_PRIVATE);
// Alternatively, if you need just one shared preference file for your activity, you can use the getPreferences() method:
// SharedPreferences sharedPref =getActivity().getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt("KEY_SCORE", newHighScore);
editor.commit();
使用SharedPreferences将用户名和密码保存在本地后,可以在\data\data\+包名+\shared_prefs目录下找到一个info.xml文件
2.Android 中的文件操作
Android 中的存储空间分为两种:Internal Storage 和 External Storage.
(1)Internal Storage
默认情况下,保存在 Internal Storage 的文件只有应用程序可见,其他应用,以及用户本⾝是无法访问这些文件的。
向 Internal Storage 写入文件的示例代码如下:
try (FileOutputStream fileOutputStream = openFileOutput(FILE_NAME, MODE_PRIVATE)) {
String str = "Hello, World!";
fileOutputStream.write(str.getBytes());
Log.i("TAG", "Successfully saved file.");
} catch (IOException ex) {
Log.e("TAG", "Fail to save file.");
}
若对应的文件不存在,openFileOutput(String, int) 函数会直接新建文件。注意传入的文件名参数不能含有 path separators(即 '/'). 该函数返回一个 FileOutputStream 对象,可以调用 write() 方法写入内容。
相应地,文件的读取可以使用 openFileInput(String) 来读取文件。该函数返回一个 FileInput- Stream,调用 read() 方法读取内容。
try (FileInputStream fileInputStream =openFileInput(FILE_NAME)) {
byte[] contents = new byte[fileInputStream.available()];
fileInputStream.read(contents);
} catch (IOException ex) {
Log.e("TAG", "Fail to read file.");
}
(2)External Storage
Android 支持使用 Java 的文件 API 来读写文件,但是关键的点在于要有一个合适的路径。如果你要存储一些公开的,体积较大的文件(如媒体文件),External Storage 就是一个比较合适的地方。如文档中所说:
All Android devices have two file storage areas: “internal” and “external” storage. These names come from the early days of Android, when most devices offered built-in non-volatile memory (internal storage), plus a removable stor- age medium such as a micro SD card (external storage). Some devices divide the permanent storage space into “internal” and “external” partitions, so even without a removable storage medium, there are always two storage spaces and the API behavior is the same whether the external storage is removable or not.
无论是否支持外置 SD 卡,所有的 Android 设备都会将存储空间分为 internal 和 external 两部分。
要往 External Storage 写入文件,需要在 AndroidManifest.xml 文件中声明权限:
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
</manifest>
随后调用 getExternalFilesDir(String type) 或 Environment.getExternalStoragePublicDirectory()来获取 SD 卡路径。两者的区别在于:前者指向的目录会在应用卸载时被删除,而后者不会。
上面的两个函数均返回一个 File 对象,代表一个目录路径,使用这个 File 对象,再结合文件名,即 可创建 FileInputStream 或 FileOutputStream 来进⾏文件读写。
举例:
void createExternalStoragePrivateFile() {
// Create a path where we will place our private file on external
// storage.
File file = new File(getExternalFilesDir(null), "DemoFile.jpg");
try {
// Very simple code to copy a picture from the application's
// resource into the external file. Note that this code does
// no error checking, and assumes the picture is small (does
// not try to copy it in chunks). Note that if external storage is // not currently mounted this will silently fail.
InputStream is =getResources().openRawResource(R.drawable.balloons);
OutputStream os = new FileOutputStream(file);
byte[] data = new byte[is.available()];
is.read(data);
os.write(data); is.close();
os.close();
} catch (IOException e) {
// Unable to create file, likely because external storage is
// not currently mounted.
Log.w("ExternalStorage", "Error writing " + file, e);
} }
实验内容
1 2 3 4
Figure 1: 首次进入, 呈现创建密码界面
Figure 2: 若密码不匹 配,弹出 Toast 提示
Figure 3: 若密码为空, 弹出 Toast 提示
Figure 4: 退出后第二次进入呈现输入密码界面
5 6 7 8
Figure 5: 若密码不正 确,弹出 Toast 提示
Figure 6: 文件加载失 败,弹出 Toast 提示
Figure 7: 成功导入文 件,弹出 Toast 提示
Figure 8: 成功保存文 件,弹出 Toast 提示
1.如 Figure 1 ⾄ Figure 8 所示,本次实验演示应用包含两个 Activity.
2.首先是密码输入 Activity:
若应用首次启动,则界面呈现出两个输入框,分别为新密码输入框和确认密码输入框。
输入框下方有两个按钮:
– OK 按钮点击后:
* 若 New Password 为空,则发出 Toast 提示。见 Figure 3。
* 若 New Password 与 Confirm Password 不匹配,则发出 Toast 提示。见 Figure 2。
* 若两密码匹配,则保存此密码,并进入文件编辑 Activity。
– CLEAR 按钮点击后:清除两输入框的内容。
• 完成创建密码后,退出应用再进入应用,则只呈现一个密码输入框。见 Figure 4。
– 点击 OK 按钮后,若输入的密码与之前的密码不匹配,则弹出 Toast 提示。见 Figure 5.
– 点击 CLEAR 按钮后,清除密码输入框的内容。
• 出于演示和学习的目的,本次实验我们使用 SharedPreferences 来保存密码。但实际应用中不会使用这种方式来存储敏感信息,而是采用更安全的机制。
3.文件编辑 Activity:
• 界面底部有三个按钮,⾼度一致,顶对齐,按钮⽔平均匀分布。三个按钮上方除 ActionBar 和 StatusBar 之外的全部空间由一个 EditText 占据(保留 margin)。EditText 内的文字竖直方 向置顶,左对齐。
• 在编辑区域输入任意内容,点击 SAVE 按钮后能保存到指定文件(文件名随意). 成功保存后,弹 出 Toast 提示。见 Figure 8。
• 点击 CLEAR 按钮,能清空编辑区域的内容。
• 点击 LOAD 按钮,能够从同一文件导入内容,并显示到编辑框中。若成功导入,则弹出 Toast。
提示。见 Figure 7。 若读取文件过程中出现异常(如文件不存在),则弹出 Toast 提示。见Figure 6。
4. 特殊要求:进入文件编辑 Activity 后,若点击返回按钮,则直接返回 Home 界面,不再返回密码输入 Activity。
参考实现
1.如何使文件编辑 Activity 的 EditText 占据上方全部空间?
使用 LinearLayout 和 layout_weight 属性。
if there are three text fields and two of them declare a weight of 1, while the other is given no weight, the third text field without weight will not grow and will only occupy the area required by its content. The other two will expand equally to fill the space remaining after all three fields are measured.
2.当 Activity 不可见时,如何将其从 activity stack 中除去(按返回键直接返回Home)?
AndroidManifest.xml 中设置 noHistory 属性。
Whether or not the activity should be removed from the activity stack and finished (its finish() method called) when the user navigates away from it and it’s no longer visible on screen —“true” if it should be finished, and “false”
if not. The default value is “false”.
3.如何根据需要隐藏/显示特定的控件?
Set visibility: You can hide or show views using setVisibility(int).
4.参考工程目录结构
实验过程
本次实验主要是实现一个备忘录,实现数据存储的功能。本次实验主要涉及SharePreferences的使用(读取和写入)。
首先,写好两个界面的XML布局文件(这里注意使用LinearLayout和layout_weigh属性,使得文件编辑Activity的EditText占据上方全部空间)。
接下来完成MainActivity.java类,在 Android 中,用于获取 SharedPreferences 的接口是 getSharedPreferences(String, int) 函数。对 SharedPreferences 的读取操作是通过 getSharedPreferences(String,int)函数返回的 SharedPreferences 对象的方法来完成的:
所有对 SharedPreferences 的写入操作,都必须通过 SharedPreferences.edit()函数返回的Editor对象来完成:
在OK按钮点击事件中,设置若 New Password为空,则发出Toast 提示。若New Password 与 Confirm Password不匹配,则发出Toast 提示,若New Password与 Confirm Password 匹配,则跳转到文件编辑界面:
获取本地存储的密码,密码不为空就隐藏其中一个密码框(保证完成创建密码后,退出应用再进入应用,则只呈现一个密码输入框):
然后在clear按钮点击事件中,通过设置tag来控制密码输入栏的清空事件(tag初始设为true,在创建密码界面中点击clear会清除两个密码输入框中的所有内容,在输入密码的登录界面中点击clear隐藏的密码框中的内容不会清除,通过与隐藏了的密码框中的已存储的密码进行比较确认输入的密码是否正确):
通过在AndroidManifest.xml中设置noHistory属性,使得当Activity不可见时,如何将其从activity stack中除去(按返回键直接返回Home):
在文件编辑的java类中,我们向Internal Storage 写入文件。
默认情况下,保存在 Internal Storage 的文件只有应用程序可见,其他应用,以及用户本身是无法 访问这些文件的。
若对应的文件不存在,openFileOutput(String, int) 函数会直接新建文件。注意传入的文件名参数不能含有 path separators(即 '/')。该函数返回一个 FileOutputStream 对象,可以调用 write() 方法写入内容(写在save点击事件中):
相应地,文件的读取可以使用 openFileInput(String) 来读取文件。该函数返回一个 FileInput- Stream,调用 read()方法读取内容(写在load点击事件中):
实现文件编辑中的文件存储和文件加载的功能。
完成实验~
实验截图
其他知识
1.Android为应用程序的存储提供了五种方式:
Shared Preferences
Internal Storage
External Storage
SQLite Database
Network Connection
2.Internal Storage 和 External Storage 的区别:
Internal storage:总是可用的;这里的文件默认是只能被自己的app所访问的;当用户卸载app的时候,系统会把internal里面的相关文件都清除干净;Internal是在你想确保不被用户与其他app所访问的最佳存储区域。Internal storage 是属于应用程序的,文件管理器看不见。
External storage:并不总是可用的,因为用户可以选择把这部分作为USB存储模式,这样就不可以访问了;是大家都可以访问的,因此保存到这里的文件是失去访问控制权限的;当用户卸载你的app时,系统仅仅会删除external根目录(getExternalFilesDir)下的相关文件;External是在你不需要严格的访问权限并且你希望这些文件能够被其他app所共享或者是允许用户通过电脑访问时的最佳存储区域。External storage 在文件浏览器里是可以看见的/mnt。
这两个概念都是相对于应用来说的,应该理解为逻辑上的概念,不应理解为物理上的外部SD卡和手机或移动设备内存。一个应用把数据存在external storage上时,那么数据成为共有的,所有人都可见的和可用的。存在internal storage上时,只有这个应用本身可以看到和使用。很多没有插SD卡的设备,系统会虚拟出一部分存储空间用来做公共存储(主要是音乐,文档之类的media)。
3.使用方法:
1)向内部存储器中创建一个私有文件并向其中写入数据,使用以下方法:
a.调用openFileOutput(String fileName, int mode)方法,
若fileName对应的文件存在,就打开该文件,若不存在,并以mode权限创建该文件并打开,该方法返回一个指向fileName对应文件的FileOutputStream,使用这个FileOutputStream可向文件中写入数据。
b.调用FileOutputStream对象的write()方法向文件中写入数据。
c.调用FileOutputStream对象的close()方法关闭文件写入流。
例:向内部存储器中写入一个名为"abc.txt"的文件后,会在内部存储器的/data/data/<package name>/files/目录下生成"abc.txt"文件。
读取内部存储器中私有文件的数据,使用以下方法:
a.调用openFileInputStream(String fileName)方法打开内部存储器中fileName对应的文件,若该文件存在,该方法返回一个指向fileName文件的FileInputStream对象。
b.调用FileInputStream对象的read()方法读取fileName文件中的内容。
c.调用FileInputStream对象的close()方法关闭文件读取流。
2)刚才保存到Internal中的时候什么都没有配置,需要保存到外部的时候需要配置读写的权限,读的权限READ_EXTERNAL_STORAGE,写的权限:READ_EXTERNAL_STORAGE。
首先需要判断一下SD卡是不是可用,因为external storage可能是不可用的比如SD卡被拔出,那么你应该在访问之前去检查是否可用。你可以通过执行 getExternalStorageState()来查询external storage的状态。如果返回的状态是MEDIA_MOUNTED, 那么你可以读写。看到这个获取完之后跟上面保存在内部存储设备的过程一样。是路径是在/mnt/sdcard目录下,如果是弄成私有文件,不允许外部访问,目录是在/mnt/sdcard/Android/data/包名 目录下。
4.其它相关:
如果想在编译时就在应用程序中保存一个不允许修改的文件,就把这个文件保存在/res/raw/目录下。
在程序中打开这个文件可以调用openRawResource(int id)方法, 里面的参数id表示R.raw.<file name>,
这个方法打开后会返回一个InputStream,使用它可以读取这个文件。这个文件不能被执行写入操作。
如果有缓存文件需要保存,而这些文件并不需要永久保存,可以调用getCacheDir()方法,该方法执行后会在内部存储器的/data/data/<package name>/目录下创建一个名为cache/的空目录(或打开cache/目录),并返回一个File对象指向这个(空)文件夹。在这个cache/目录下,可以保存缓存文件,当设备的内部存储器空间不够用时,系统会自动删除一部分cache/目录下的缓存文件,但为了保证系统运行效率,应该手动对cache/目录的大小进行控制,如控制它不能大于1M。当用户卸载应用程序时,cache/目录会连同一起被删除。
例:调用getCacheDir()方法,该方法执行后会在内部存储器的/data/data/<package name>/目录下创建一个名为cache/的空目录。
源码下载
源码下载点击这里~
注
1、本实验实验环境:
操作系统 Windows 10
实验软件 Android Studio 2.2.1
虚拟设备:Nexus_5X
API:23
2、贴代码的时候由于插入代码框的大小问题,代码格式不太严整,望见谅~
Android开发7:简单的数据存储(使用SharedPreferences)和文件操作的更多相关文章
- Android学习之简单的数据存储
在Android中,数据存储是开发人员不可以避免的.Android为开发者提供了很多的存储方法,在前面的博客中,已经讲述了sqlite存储数据.今天将介绍用SharedPreferences来存储数据 ...
- android菜鸟学习笔记18----Android数据存储(二)SharedPreferences
数据存储的方式,有比直接文件读写更加简便的方式,那就是操作SharedPreferences. SharedPreferences一般用于存储用户的偏好设定,暂时不支持多进程操作. SharedPre ...
- android开发中的5种存储数据方式
数据存储在开发中是使用最频繁的,根据不同的情况选择不同的存储数据方式对于提高开发效率很有帮助.下面笔者在主要介绍Android平台中实现数据存储的5种方式. 1.使用SharedPreferences ...
- Android系统的五种数据存储形式(二)
之前介绍了Android系统下三种数据存储形式,今天补充介绍另外两种,分别是内容提供者和网络存储.有些人可能认为内存提供者和网络存储更偏向于对数据的操作而不是数据的存储,但这两种方式确实与数据有关,所 ...
- 【iOS-Android开发对照】之 数据存储
[iOS-Android开发对照]之 数据存储 写在前面的话 相比Android和iOS,我认为Android的数据存储更开放一些.Android天生就能够使用多Java I/O:并且天生开放的特性, ...
- Android系统的五种数据存储形式(一)
Android系统有五种数据存储形式,分别是文件存储.SP存储.数据库存储.contentprovider 内容提供者.网络存储.其中,前四个是本地存储.存储的类型包括简单文本.窗口状态存储.音频视频 ...
- Android 数据存储之 SharedPreferences储存
------------------------------------------SharedPreferences存储--------------------------------------- ...
- Android简易数据存储之SharedPreferences
Andorid提供了多种数据存储的方式,例如前面说到的“Android数据存储之SQLite的操作”是用于较复杂的数据存储.然而,如果有些简单的数据存储如果采用SQLite的方式的话会显得比较笨重.例 ...
- Android数据存储三剑客——SharedPreferences、File、SQLite
Android中常用的数据存储一般有三种方式:SharedPreferences.文件和SQLite数据库,用来保存需要长时间保存的数据.本文将通过几个具体的小实例来讲解这三种方式的具体实现. 数据存 ...
- Android数据存储-通过SharedPreferences实现记住密码的操作
在Android中登陆中,为了实现用户的方便,往往需要根据用户的需要进行记住密码的操作,所以,在Android数据存储中SharedPreferences恰恰可以实现这一点 下面,小编将带领大家通过S ...
随机推荐
- jQuery 2.0.3 源码分析Sizzle引擎 - 高效查询
为什么Sizzle很高效? 首先,从处理流程上理解,它总是先使用最高效的原生方法来做处理 HTML文档一共有这么四个API: getElementById 上下文只能是HTML文档 浏览器支持情况:I ...
- 深入理解闭包系列第三篇——IIFE
× 目录 [1]实现 [2]用途 前面的话 严格来讲,IIFE并不是闭包,因为它并不满足函数成为闭包的三个条件.但一般地,人们认为IIFE就是闭包,毕竟闭包有多个定义.本文将详细介绍IIFE的实现和用 ...
- 【原创】开源Math.NET基础数学类库使用(17)C#计算矩阵条件数
本博客所有文章分类的总目录:[总目录]本博客博文总目录-实时更新 开源Math.NET基础数学类库使用总目录:[目录]开源Math.NET基础数学类库使用总目录 上个月 ...
- Util应用程序框架公共操作类(八):Lambda表达式公共操作类(二)
前面介绍了查询的基础扩展,下面准备给大家介绍一些有用的查询封装手法,比如对日期范围查询,数值范围查询的封装等,为了支持这些功能,需要增强公共操作类. Lambda表达式公共操作类,我在前面已经简单介绍 ...
- wireshark常用的过滤器设置
过滤源ip.目的ip.在wireshark的过滤规则框Filter中输入过滤条件.如查找目的地址为192.168.101.8的包,ip.dst==192.168.101.8:查找源地址为ip.src ...
- C++ 连接数据库的入口和获取列数、数据
这里不具体放出完整的程序,分享两个核心函数: 由于这里用到的函数是编译器自己的库所没有的,需要自己下载mysql.h库或者本地有数据库,可以去bin找到,放进去. 前提,我自己的测试数据库是WampS ...
- 千呼万唤始出来:Apache Spark2.0正式发布
我们很荣幸地宣布,自7月26日起Databricks开始提供Apache Spark 2.0的下载,这个版本是基于社区在过去两年的经验总结而成,不但加入了用户喜爱的功能,也修复了之前的痛点. 本文总结 ...
- ZOJ Problem Set - 1006 Do the Untwist
今天在ZOJ上做了道很简单的题目是关于加密解密问题的,此题的关键点就在于求余的逆运算: 比如假设都是正整数 A=(B-C)%D 则 B - C = D*n + A 其中 A < D 移项 B = ...
- OpenCV2:等间隔采样和局部均值的图像缩小
图像的缩小从物理意义上来说,就是将图像的每个像素的大小缩小相应的倍数.但是,改变像素的物理尺寸显然不是那么容易的,从数字图像处理的角度来看,图像的缩小实际就是通过减少像素个数来实现的.显而易见的,减少 ...
- 你必须知道ASP.NET知识------关于动态注册httpmodule(对不起汤姆大叔)
一.关于动态注册的问题 很多人看过汤姆大叔的MVC之前的那点事儿系列(6):动态注册HttpModule ,其实汤姆大叔没有发现httpmodule动态注册的根本机制在哪里. 亦即:怎么动态注册?为什 ...