本节例程下载地址:WillFlowFile
什么是持久化技术?
数据持久化就是指将那些内存中的瞬时数据保存到存储设备中,保证即使在手机或电脑关机的情况下,这些数据仍然不会丢失。保存在内存中的数据是处于瞬时状态的,而保存在存储设备中的数据是处于持久状态的,持久化技术则是提供了一种机制可以让数据在瞬时状态和持久状态之间进行转换。
持久化技术被广泛应用于各种程序设计的领域当中,而本篇要探讨的自然是 Android 中的数据持久化技术。 Android 系统中主要提供了三种方式用于简单地实现数据持久化功能,即文件存储、 haredPreference 存储以及数据库存储。当然,除了这三种方式之外,你还可以将数据保存在手机的 SD 卡中,不过使用文件、 SharedPreference 或 数据库来保存数据会相对更简单一些,而且比起将数据保存在 SD 卡中会更加的安全。
一、文件存储
文件存储是 Android 中最基本的一种数据存储方式,它不对存储的内容进行任何的格式化处理,所有数据都是原封不动地保存到文件当中的,因而它比较适合用于存储一些简单的文本数据或二进制数据。如果w我们想使用文件存储的方式来保存一些较为复杂的文本数据,就需要定义一套自己的格式规范,这样方便于之后将数据从文件中重新解析出来。
1、将数据存储到文件中
Context 类中提供了一个 openFileOutput() 方法,可以用于将数据存储到指定的文件中。这个方法接收两个参数:
-
第一个参数是文件名,在文件创建的时候使用的就是这个名称。
注意这里指定的文件名不可以包含路径,因为所有的文件都是默认存储到 /data/data/<packagename>/files/ 目录下的 。 -
第二个参数是文件的操作模式,主要有两种模式可选,MODE_PRIVATE 和 MODE_APPEND。
其中 MODE_PRIVATE 是默认的操作模式,表示当指定同样文件名的时候,所写入的内容将会覆盖原文件中的内容,而 MODE_APPEND 则表示如果该文件已存在就往文件里面追加内容,不存在就创建新文件。其实文件的操作模式本来还有另外两种,MODE_WORLD_READABLE 和 ODE_WORLD_WRITEABLE,这两种模式表示允许其他的应用程序对我们程序中的文件进行读写操作,不过由于这两种模式过于危险,很容易引起应用的安全性漏洞,现已在 Android 4.2 版本中被废弃。
openFileOutput ()方法返回的是一个 FileOutputStream 对象,得到了这个对象之后就可以使用 Java 流的方式将数据写入到文件中了。
以下的代码展示了如何将一段文本保存到文件中:
public void save() {
String dataString = "This is the data to save.";
FileOutputStream fileOutputStream = null;
BufferedWriter bufferedWriter = null;
try {
fileOutputStream = openFileOutput("fileData", Context.MODE_PRIVATE);
bufferedWriter = new BufferedWriter(new OutputStreamWriter(fileOutputStream));
bufferedWriter.write(dataString);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bufferedWriter != null) {
bufferedWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
这里通过 openFileOutput() 方法能够得到一个 FileOutputStream 对象,然后再借助它构建出一个 OutputStreamWriter 对象,接着再使用 OutputStreamWriter 构建出一个 BufferedWriter 对象,这样我们就可以通过 BufferedWriter 来将文本内容写入到文件中了。下面通过一个完整的示例来展示 Android 中的文件存储的使用。
修改 activity_main.xml 中的代码,如下所示:
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.wgh.willflowfile.MainActivity">
<EditText
android:id="@+id/edit_file"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="输入要保存的文本"
android:textColor="#007dfa"
android:textColorHint="#898787"
android:textSize="21dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.109" />
<Button
android:id="@+id/button_save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="点击保存文本"
android:textColor="#f56200"
android:textColorHint="#898787"
android:textSize="22dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.267" />
</android.support.constraint.ConstraintLayout>
我们在布局中加入了一个 EditText,用于输入文本内容,加了一个按钮,用于保存文本内容。然后在文本输入框中随意输入点什么内容,再按下 Back 键,这时输入的内容肯定就已经丢失了,因为它只是瞬时数据,在Activity被销毁后就会被回收。而这里我们要做的,就是在数据被回收之前,将它存储到文件当中。
修改MainActivity 中的代码,如下所示:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private EditText mEditText;
private Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mEditText = (EditText) findViewById(R.id.edit_file);
mButton = (Button) findViewById(R.id.button_save);
mButton.setOnClickListener(this);
}
public void saveToFile(String sourceString) {
FileOutputStream fileOutputStream = null;
BufferedWriter bufferedWriter = null;
try {
fileOutputStream = openFileOutput("fileData", Context.MODE_PRIVATE);
bufferedWriter = new BufferedWriter(new OutputStreamWriter(fileOutputStream));
bufferedWriter.write(sourceString);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bufferedWriter != null) {
bufferedWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
saveToFile(mEditText.getText().toString());
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.button_save:
saveToFile(mEditText.getText().toString());
break;
}
}
}
首先我们在 onCreate() 方法中获取了 EditText 的实例,然后重写了 onDestroy() 方法,这样就可以保证在Activity销毁之前一定会调用这个方法。然后我们在 Button 的点击事件中调用 save() 方法把输入的内容存储到文件中,并且在 onDestroy() 方法中我们采用同样的方法保存文本到文件中,文件命名为 fileData。
运行程序看效果:
然后 Button 保存文本或者按下 Back 键关闭程序,这时我们输入的内容就已经保存到文件中了,然后我们通过 ADB 命令进入文件系统查看我们保存的文件:
ADB 的相关介绍,可以移步这里:5.1 常用工具之ADB介绍与快速入门
这样就证实了在 EditText 中输入的内容确实已经成功保存到文件中了。不过只是成功将数据保存下来还不够,我们还需要想办法在下次启动程序的时候让这些数据能够还原到 EditText 中,因此接下来我们就从文件中读取数据。
2、从文件中读取数据
类似于将数据存储到文件中,Context 类中还提供了一个 openFileInput() 方法,用于从文件中读取数据。这个方法要比 openFileOutput() 简单一些,它只接收一个参数,即要读取的文件名,然后系统会自动到 /data/data/<package name>/files/ 目录下去加载这个文件,并返回一个 FileInputStream 对象,得到了这个对象之后再通过 Java 流的方式就可以将数据读取出来了。
以下代码展示了如何从文件中读取文本数据:
public String loadFromFile() {
FileInputStream fileInputStream = null;
BufferedReader bufferedReader = null;
StringBuilder stringBuilder = new StringBuilder();
try {
fileInputStream = openFileInput("fileData");
bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));
String readLine = "";
while ((readLine = bufferedReader.readLine()) != null) {
stringBuilder.append(readLine);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return stringBuilder.toString();
}
首先通过 openFileInput( )方法获取到了一个 FileInputStream 对象,然后借助它又构建出了一个 InputStreamReader 对象,接着再使用 InputStreamReader 构建出一个BufferedReader 对象,这样我们就可以通过 BufferedReader 进行一行行地读取,把文件中所有的文本内容全部读取出来并存放在一个 StringBuilder 对象中,最后将读取到的内容返回就可以了。我们来继续完善上面的例子,使得重新启动程序时 EditText 中能够保留我们上次输入的内容。
修改 MainActivity 中的代码,如下所示:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText mEditText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mEditText = (EditText) findViewById(R.id.edit_file);
String fileString = loadFromFile();
if (!TextUtils.isEmpty(fileString)) {
mEditText.setText(fileString);
mEditText.setSelection(fileString.length());
Toast.makeText(MainActivity.this, "获得文件文本:" + fileString, Toast.LENGTH_SHORT).show();
}
}
public String loadFromFile() {
FileInputStream fileInputStream = null;
BufferedReader bufferedReader = null;
StringBuilder stringBuilder = new StringBuilder();
try {
fileInputStream = openFileInput("fileData");
bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));
String readLine = "";
while ((readLine = bufferedReader.readLine()) != null) {
stringBuilder.append(readLine);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return stringBuilder.toString();
}
}
我们在 onCreate() 方法中调用 loadFromFile() 方法来读取文件中存储的文本内容,如果读到的内容不为空,就调用 EditText 的 setText() 方法将内容填充到 EditText 里,并调用 setSelection 方法将输入光标移动到文本的末尾位置以便于继续输入,然后弹出一句还原成功的提示。
注意:上述代码在对字符串进行非空判断的时候使用了 TextUtils.isEmpty() 方法,这是一个非常好用的方法,它可以一次性进行两种空值的判断。当传入的字符串等于 null 或者等于空字符串的时候,这个方法都会返回 true,从而使得我们不需要单独去判断这两种空值,再使用逻辑运算符连接起来了。
编译运行看效果:
这样我们就已经把文件存储方面的知识学习完了,其实所用到的核心技术就是 Context 类中提供的 openFileInput() 和 openFileOutput() 方法,之后就是利用 Java 的各种流来进行读写操作就可以了。
点此进入:GitHub开源项目“爱阅”。
感谢优秀的你跋山涉水看到了这里,欢迎关注下让我们永远在一起!