android: 文件存储
数据持久化就是指将那些内存中的瞬时数据保存到存储设备中,保证即使在手机或电脑 关机的情况下,这些数据仍然不会丢失。保存在内存中的数据是处于瞬时状态的,而保存在 存储设备中的数据是处于持久状态的,持久化技术则是提供了一种机制可以让数据在瞬时状 态和持久状态之间进行转换。
持久化技术被广泛应用于各种程序设计的领域当中,而本书中要探讨的自然是 Android 中的数据持久化技术。Android 系统中主要提供了三种方式用于简单地实现数据持久化功能, 即文件存储、SharedPreference 存储以及数据库存储。当然,除了这三种方式之外,你还可 以将数据保存在手机的 SD 卡中,不过使用文件、SharedPreference 或数据库来保存数据会相 对更简单一些,而且比起将数据保存在 SD 卡中会更加的安全。
文件存储是 Android 中最基本的一种数据存储方式,它不对存储的内容进行任何的格式 化处理,所有数据都是原封不动地保存到文件当中的,因而它比较适合用于存储一些简单的 文本数据或二进制数据。如果你想使用文件存储的方式来保存一些较为复杂的文本数据,就 需要定义一套自己的格式规范,这样方便于之后将数据从文件中重新解析出来。
那么首先我们就来看一看,Android 中是如何通过文件来保存数据的。
6.2.1 将数据存储到文件中
Context 类中提供了一个 openFileOutput ()方法,可以用于将数据存储到指定的文件中。 这个方法接收两个参数,第一个参数是文件名,在文件创建的时候使用的就是这个名称,注 意这里指定的文件名不可以包含路径,因为所有的文件都是默认存储到/data/data/<package name>/files/ 目 录 下 的 。 第 二 个 参 数 是 文 件 的 操 作 模 式 , 主 要 有 两 种 模 式 可 选 , MODE_PRIVATE 和 MODE_APPEND。其中 MODE_PRIVATE 是默认的操作模式,表示当指 定同样文件名的时候,所写入的内容将会覆盖原文件中的内容,而 MODE_APPEND 则表示 如果该文件已存在就往文件里面追加内容,不存在就创建新文件。其实文件的操作模式本来 还有另外两种,MODE_WORLD_READABLE 和 MODE_WORLD_WRITEABLE,这两种模 式表示允许其他的应用程序对我们程序中的文件进行读写操作,不过由于这两种模式过于危 险,很容易引起应用的安全性漏洞,现已在 Android 4.2 版本中被废弃。
openFileOutput ()方法返回的是一个 FileOutputStream 对象,得到了这个对象之后就可以 使用 Java 流的方式将数据写入到文件中了。以下是一段简单的代码示例,展示了如何将一 段文本内容保存到文件中:
public void save() {
String data = "Data to save"; FileOutputStream out = null; BufferedWriter writer = null; try {
out = openFileOutput("data", Context.MODE_PRIVATE);
writer = new BufferedWriter(new OutputStreamWriter(out));
writer.write(data);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
如 果 你 已 经 比 较 熟 悉 Java 流 了 , 理 解 上 面 的 代 码 一 定 轻 而 易 举 吧 。 这 里 通 过 openFileOutput() 方 法 能 够 得 到 一 个 FileOutputStream 对 象 , 然 后 再 借 助 它 构 建 出 一 个 OutputStreamWriter 对象,接着再使用 OutputStreamWriter 构建出一个 BufferedWriter 对象, 这样你就可以通过 BufferedWriter 来将文本内容写入到文件中了。
下面我们就编写一个完整的例子,借此学习一下如何在 Android 项目中使用文件存储的
技术。首先创建一个 FilePersistenceTest 项目,并修改 activity_main.xml 中的代码,如下所示:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent" >
<EditText
android:id="@+id/edit" android:layout_width="match_parent"
android:layout_height="wrap_content" android:hint="Type
something here"
/>
</LinearLayout>
这里只是在布局中加入了一个 EditText,用于输入文本内容。其实现在你就可以运行一
下程序了,界面上肯定会有一个文本输入框。然后在文本输入框中随意输入点什么内容,再 按下 Back 键,这时输入的内容肯定就已经丢失了,因为它只是瞬时数据,在活动被销毁后 就会被回收。而这里我们要做的,就是在数据被回收之前,将它存储到文件当中。修改 MainActivity 中的代码,如下所示:
public class MainActivity extends Activity {
private EditText edit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
edit = (EditText) findViewById(R.id.edit);
}
@Override
protected void onDestroy() {
super.onDestroy();
String inputText = edit.getText().toString();
save(inputText);
}
public void save(String
inputText) { FileOutputStream out = null; BufferedWriter writer = null;
try {
out = openFileOutput("data", Context.MODE_PRIVATE);
writer = new BufferedWriter(new OutputStreamWriter(out));
writer.write(inputText);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
可以看到,首先我们在 onCreate()方法中获取了 EditText 的实例,然后重写了 onDestroy()
方法,这样就可以保证在活动销毁之前一定会调用这个方法。在 onDestroy()方法中我们获取
了 EditText 中输入的内容,并调用 save()方法把输入的内容存储到文件中,文件命名为 data。
save()方法中的代码和之前的示例基本相同,这里就不再做解释了。现在重新运行一下程序,
并在 Editext 中输入一些内容,如图 6.1 所示。
图 6.1
然后按下 Back 键关闭程序,这时我们输入的内容就已经保存到文件中了。那么如何才 能证实数据确实已经保存成功了呢?我们可以借助 DDMS 的 File Explorer 来查看一下。切换 到 DDMS 视 图 , 并 点 击 File Explorer 切 换 卡 , 在 这 里 进 入 到 /data/data/com.example. filepersistencetest/files/目录下,可以看到生成了一个 data 文件,如图 6.2 所示。
图 6.2
然后点击图 6.3 中最左边的按钮可以将这个文件导出到电脑上。
图 6.3
使用记事本打开这个文件,里面的内容如图 6.4 所示。
图 6.4
这样就证实了,在 EditText 中输入的内容确实已经成功保存到文件中了。
不过只是成功将数据保存下来还不够,我们还需要想办法在下次启动程序的时候让这些
数据能够还原到 EditText 中,因此接下来我们就要学习一下,如何从文件中读取数据。
6.2.2 从文件中读取数据
类似于将数据存储到文件中,Context 类中还提供了一个 openFileInput()方法,用于从文
件中读取数据。这个方法要比 openFileOutput()简单一些,它只接收一个参数,即要读取的文
件名,然后系统会自动到/data/data/<package name>/files/目录下去加载这个文件,并返回一个
FileInputStream 对象,得到了这个对象之后再通过 Java 流的方式就可以将数据读取出来了。
以下是一段简单的代码示例,展示了如何从文件中读取文本数据:
public String load() {
FileInputStream in = null; BufferedReader reader = null;
StringBuilder content = new StringBuilder();
try {
in = openFileInput("data");
reader = new BufferedReader(new
InputStreamReader(in)); String line = "";
while ((line = reader.readLine()) != null) {
content.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return content.toString();
}
在这段代码中,首先通过 openFileInput()方法获取到了一个 FileInputStream 对象,然后 借助它又构建出了一个 InputStreamReader 对象,接着再使用 InputStreamReader 构建出一个
BufferedReader 对象,这样我们就可以通过 BufferedReader 进行一行行地读取,把文件中所 有的文本内容全部读取出来并存放在一个 StringBuilder 对象中,最后将读取到的内容返回就
可以了。
了解了从文件中读取数据的方法,那么我们就来继续完善上一小节中的例子,使得重新
启动程序时 EditText 中能够保留我们上次输入的内容。修改 MainActivity 中的代码,如下所示:
public class MainActivity extends Activity {
private EditText edit;
@Override
protected void
onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
edit = (EditText) findViewById(R.id.edit);
String inputText = load();
if
(!TextUtils.isEmpty(inputText)) { edit.setText(inputText);
edit.setSelection(inputText.length()); Toast.makeText(this, "Restoring
succeeded",
Toast.LENGTH_SHORT).show();
}
}
……
public String load() {
FileInputStream in = null; BufferedReader reader = null;
StringBuilder content = new StringBuilder();
try {
in = openFileInput("data");
reader = new BufferedReader(new
InputStreamReader(in)); String line = "";
while ((line = reader.readLine()) != null) {
content.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return content.toString();
}
}
可以看到,这里的思路非常简单,在 onCreate()方法中调用 load()方法来读取文件中存储
的文本内容,如果读到的内容不为空,就调用 EditText 的 setText()方法将内容填充到 EditText
里,并调用 setSelection 方法将输入光标移动到文本的末尾位置以便于继续输入,然后弹出 一句还原成功的提示。load()方法中的细节我们前面已经讲过了,这里就不再赘述。
注意上述代码在对字符串进行非空判断的时候使用了 TextUtils.isEmpty()方法,这是一 个非常好用的方法,它可以一次性进行两种空值的判断。当传入的字符串等于 null 或者等于 空字符串的时候,这个方法都会返回 true,从而使得我们不需要单独去判断这两种空值,再
使用逻辑运算符连接起来了。
现在重新运行一下程序,刚才保存的 Content 字符串肯定会被填充到 EditText 中,然后编写一点其他的内容,比如在 EditText 中输入 Hello,接着按下 Back 键退出程序,再重新启
动程序,这时刚才输入的内容并不会丢失,而是还原到了 EditText 中,如图 6.5 所示。
图 6.5
这样我们就已经把文件存储方面的知识学习完了,其实所用到的核心技术就是 Context 类中提供的 openFileInput()和 openFileOutput()方法,之后就是利用 Java 的各种流来进行读写
操作就可以了。
不过正如我前面所说,文件存储的方式并不适合用于保存一些较为复杂的文本数据,因 此,下面我们就来学习一下 Android 中另一种数据持久化的方式,它比文件存储更加简单易
用,而且可以很方便地对某一指定的数据进行读写操作。
android: 文件存储的更多相关文章
- Android 文件存储浅析
最近做的一个需求和文件存储有关系.由于之前没有系统梳理过,对文件存储方面的知识一直很懵懂.趁着周末有时间,赶紧梳理一波. 这首从网上找到的一张图,很好的概括了外部存储和内部存储. 下面我们再来具体介绍 ...
- Android文件存储
文件存储是Android中最基本的一种数据存储方式,它不读存储的内容进行任何的格式化处理,所有数据原封不动的保存在文件之中.如果想用文件存储的方式保存一些较为复杂的数据,就需要定义一套自己的格式规范, ...
- android文件存储位置切换
最近有个需求,助手的google卫星地图和OpenCycleMap下载的离线地图数据,要能够在内置存储和外置存储空间之间切换,因为离线瓦片数据非常大,很多户外用户希望将这些文件存储在外置TF卡上,不占 ...
- 转:Android文件存储路径getFilesDir()与getExternalFilesDir的区别
作为一个开发者,我们经常需要通过缓存一些文件到SD卡中,常见的方式就是,通过: File sdCard = Environment.getExternalStorageDirectory(); 获取S ...
- android 文件存储&SharedPreferences
一.文件存储 文件存储主要是I/O流的操作,没什么好说的,需要注意的是保存文件的各个目录. 下面为常用的目录: public static File getInFileDir(Context cont ...
- 程序员带你学习安卓开发系列-Android文件存储
这是程序员带你学习安卓开发系列教程.本文章致力于面向对象程序员可以快速学习开发安卓技术. 上篇文章:.Net程序员快速学习安卓开发-布局和点击事件的写法 主要讲解了布局和点击事件的写法. 上篇文章补充 ...
- Android文件存储使用参考
可能遇到的问题 android系统自身自带有存储,另外也可以通过sd卡来扩充存储空间.前者好比pc中的硬盘,后者好移动硬盘. 前者空间较小,后者空间大,但后者不一定可用. 开发应用,处理本地数据存取时 ...
- Android 文件存储 和 权限管理
转载请标明出处: :http://blog.csdn.net/huaiyiheyuan/article/details/52473984 android SD卡主要有两种存储方式 Internal . ...
- Android数据存储之Android 6.0运行时权限下文件存储的思考
前言: 在我们做App开发的过程中基本上都会用到文件存储,所以文件存储对于我们来说是相当熟悉了,不过自从Android 6.0发布之后,基于运行时权限机制访问外置sdcard是需要动态申请权限,所以以 ...
随机推荐
- DNS劫持和DNS污染的区别
我们知道,某些网络运营商为了某些目的,对DNS进行了某些操作,导致使用ISP的正常上网设置无法通过域名取得正确的IP地址.常用的手段有:DNS劫持和DNS污染. 什么是DNS劫持 DNS劫持就是通过劫 ...
- Android消息机制:Looper,MessageQueue,Message与handler
Android消息机制好多人都讲过,但是自己去翻源码的时候才能明白. 今天试着讲一下,因为目标是讲清楚整体逻辑,所以不追究细节. Message是消息机制的核心,所以从Message讲起. 1.Mes ...
- Java中的转义字符
1.转义字符 1.八进制转义序列:\ + 1到3位5数字:范围'\000'~'\377' \0:空字符 2.Unicode转义字符:\u + 四个十六进制数字:0~65535 \u ...
- C# 字符串替换Replace
C# 中的石strA.Replace(strB,strC)函数可以实现将strA中的strB替换为strC. 但是容易出错的地方是,这并不是就直接替换好了,此函数的返回值才是替换好的字符串,所以还要要 ...
- 传值 属性 block 单例 协议
界面传值 四种传值的方式 1.属性传值(从前往后) 步骤: 1.属性传值用于第一个界面向第二个界面传值 2.明确二者联系的桥梁,也就是触发跳转的地方 3.明确传输的值,类型是什么 4.在第二个视图控制 ...
- Java垃圾回收小结
一.如何确定某个对象是“垃圾”? 首先要搞清一个最基本的问题:如果确定某个对象是“垃圾”?既然垃圾收集器的任务是回收垃圾对象所占的空间供新的对象使用,那么垃圾收集器如何确定某个对象是“垃圾”?—即通过 ...
- 【Visual Lisp】驱动器、目录、文件和注册表
;;驱动器.目录.文件.和注册表;;★★★01.获取并创建驱动器盘符组成的表★★★(setq Drives (vlax-get-property (vlax-create-object "S ...
- 基于ticket的rw锁
代码: wiredtiger-2.8.0/src/os_posix/os_mtx_rw.c rw锁结构 struct { uint16_t writers; // Now serving for wr ...
- tomcat 协议之并发协议 Http11NioProtocol
关于此协议的原理是什么尚不明确,待后续学习,但是该协议(Http11NioProtocol)能够改善高并发时tomcat的性能. 默认为HTTP/1.1,也就是阻塞式,在改用org.apache.co ...
- PHP程序设计经典300例
不知道怎么转载,原文源自:http://bbs.php100.com/u-htm-uid-330857.html 来自:php100钟泽锋 第一例<?php $s_html="< ...