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 中另一种数据持久化的方式,它比文件存储更加简单易 用,而且可以很方便地对某一指定的数据进行读写操作。

posted @ 2016-03-03 14:13  dodo-yufan  阅读(643)  评论(0编辑  收藏  举报