4.1.1 持久化技术概述及文件存储的用法大全

本节例程下载地址: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开源项目“爱阅”

感谢优秀的你跋山涉水看到了这里,欢迎关注下让我们永远在一起!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容