前言
由于项目需要使用到涉及到Android文件的保存及读取
,那就学呗。
原先的计划是使用文本
进行保存,数据格式使用Json
。
后面发现了SharedPreferences存储
,一步到位,我喜欢。
内存&外存 1
开始之前,我们先来补充一下安卓中内部存储
及外部存储
的概念。
当然标题所说的内存
并不是手机上的运行内存
…而是指手机的内部存储
。
内部存储
位于系统中很特殊的一个位置,如果你想将文件存储于内部存储
中,那么文件默认只能被你的应用访问到,且一个应用所创建的所有文件都在和应用包名相同的目录
下。也就是说应用创建于内部存储
的文件,与这个应用是关联起来的。当一个应用卸载之后,内部存储
中的这些文件也被删除。
至于外部存储,在4.4(API19)
之前,将手机自身带的存储卡划分为内部存储
,而扩展的SD卡
就是外部存储
。但是从Android4.4
开始,Google
将机身存储(内置存储)
在概念上分成了内部存储 (internal)
和外部存储 (external)
两部分。
那么怎么区分机身存储
的外部存储
跟SD卡
的外部存储
呢?对,SD卡
也是外部存储
,那怎么区分呢,在Android 4.4
以后的系统中,API
提供了这样一个方法来遍历手机的外部存储路径:
File[] files;
//API > 19(Android 4.4)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
files = getExternalFilesDirs(Environment.MEDIA_MOUNTED);
for(File file:files){
Log.e("main", String.valueOf(file));
}
}
荣耀小7遍历结果:
可以看到它们都位于/storage
下:
-
/storage/emulated/0
是机身存储 -
/storage/BE16-1E17
是外部存储
关于文件路径,可能需要先了解一下linux文件挂载
,这里不展开。
由于内部存储
相对于外部存储
更外宝贵,因而文件存储的常规操作是:先检测有无外部存储(SD卡),无外部存储时再使用机身存储。
SharedPreferences
不同于文件存储,SharedPreferences
使用键值对
的形式保存,即在保存时需要提供一个字符串作为键值,在读取数据时就可以通过键值把对应的数据读取出来。官方文档提醒您:该类不支持跨进程使用。
存储格式:XML
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<!--类型 name = "键值" value = "数据"/-->
<int name="25" value="625" />
</map>
保存
想要使用SharedPreferences
进行存储,首先需要获取到SharedPreferences对象
。
上面的例子,使用的是Context类
中的getSharedPreferences()
方法。
SharedPreferences getSharedPreferences (String name,int mode)
其他参数如下:
name
:文件名,如果指定的文件不在,则会创建一个。mode
:模式,目前只有MODE_PRIVATE
一种可选项,当然使用0
的效果也是一样的。
-
MODE_PRIVATE
:只有当前应用程序
才能对这个SharedPreferences
文件进行读写。 -
MODE_WORLD_READABLE
:Android 4.2+ 弃用 -
MODE_WORLD_WRITEABLE
:Android 4.2+ 弃用 -
MODE_MULTI_PROCESS
:Android 6.0+ 弃用
除此之外,我们还可以通过以下方法获取SharedPreferences对象
:Activity
类的getPreferences()
方法。
SharedPreferences getPreferences(@Context.PreferencesMode int mode)
该方法与getSharedPreferences()
类似,但它只有一个参数mode
,文件名则是当前Activity
的类名。
取得SharedPreferences对象
后,我们可以通过edit()
方法获取其Edit对象
,通过Edit对象
可以修改内存中的SharedPreferences
的数据,再原子地提交到硬盘SharedPreferences
。(没有理解错的话)
例如我们要添加文本数据,则使用putString()
;需要添加整型数据,则使用putint()
…就不一一列举了。
此外,Edit类
还为我们提供了clear()
方法以清空数据。
修改完以后就是提交
,目前提交有两种方法:
-
apply()
:立即将其更改提交到内存
,对机身存储进行异步提交,且不会收到任何故障警告。 -
commit()
:同步地写到持久性存储SharedPreferences
中,因此我们应避免在主线程中直接使用它。
下面给出一个简单的文件保存示例:
sharedPreferences = getSharedPreferences("SharedPreferences", MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("test",editText.getText().toString());
editor.apply();
//editor.commit();
保存路径(需root用户才能看到)
那么保存的文件存储再哪里呢?
目前使用模拟器寻得的路径为:/data/data/pagename(包名)/shared_prefs
而在未获取root权限
的手机上是无法直接查看到该文件的。
而目前接触的真机基本都是未获取root权限
的;手机上看不到文件,心里虚得慌怎么办?
放心,我们可以在读取文件的地方debug
一下,直接查看获取到的SharedPreferences对象
。
可以发现,该demo
在荣耀小7
上运行时,文件是存储在机身存储
上的。
读取
可能要让你失望了,读取的方法比保存更为简单。
首先同样是取得SharedPreferences对象
,这点和上面是一样的。
随后我们就可以通过get()
方法取得我们所需要的数据(只要它在SharedPreferences中)。
每一个Editor
的put()
方法,在SharedPreferences
中都能找到一个get()
方法将数据取出。
下面直接看例子:
String value = sharedPreferences.getString("test","Null");
textView.setText(value);
demo效果
这是在学习之前,自己问自己的问题,这里给出答案
Q:保存为同名
SharedPreferences文件
,会造成文件覆盖吗?
若文件已存在则不会覆盖,而是追加内容。
若追加同键值内容则会造成覆盖,否则添加新的键值对。
局限性 2:
SharedPreferences
虽好,但不可滥用
value
内容不宜过大
- 第一次从
SharedPreferences
中获取值的时候,有可能阻塞主线程
,使界面卡顿、掉帧。 - 解析
SharedPreferences
的时候会产生大量的临时对象
,导致频繁GC,引起界面卡顿。 - 这些
key
和value
会永远存在于内存
之中,不会被释放。
- 存储
JSON
等特殊符号很多的value
-
JSON
或者HTML
格式存放在SharedPreferences
里面的时候,需要转义,这样会带来很多&
这种特殊符号,SharedPreferences
在解析碰到这个特殊符号的时候会进行特殊的处理
,引发额外的字符串拼接以及函数调用开销。
- 多次
edit()
多次apply()
- 每次
edit()
都会创建一个Editor对象
,额外占用内存
;当然多创建几个对象也影响不了多少;但是,多次apply()
也会卡界面你造吗?
- 至于
跨进程
,这个SharedPreferences类
开篇就澄清了, 不支持跨线程。
而容易让人引起误解的MODE_MULTI_PROCESS
模式也于Android6.0弃用
了。
结语
本文只是简单的介绍了如何使用SharedPreferences
进行文件的读写。在此需要注意的是,当设备内部空间不足时,Android
可能会删除缓存文件回收空间,但是我们不应该依赖系统为我们清理这些文件,而应始终自行维护缓存文件,使其占用空间保持在合理的范围内(例如1M)。因此我的操作是,每次打开文件写入前,先用clear()
清空。