1.1 持久化技術概述及文件存儲的用法大全

點此進入:從零快速構建APP系列目錄導圖
點此進入:UI編程系列目錄導圖
點此進入:四大組件系列目錄導圖
點此進入:數據網絡和線程系列目錄導圖
本節例程下載地址:WillFlowFile
什麼是持久化技術?

數據持久化就是指將那些內存中的瞬時數據保存到存儲設備中,保證即使在手機或電腦關機的情況下,這些數據仍然不會丟失。保存在內存中的數據是處於瞬時狀態的,而保存在存儲設備中的數據是處於持久狀態的,持久化技術則是提供了一種機制可以讓數據在瞬時狀態和持久狀態之間進行轉換。

持久化技術被廣泛應用於各種程序設計的領域當中,而本篇要探討的自然是 Android 中的數據持久化技術。 Android 系統中主要提供了三種方式用於簡單地實現數據持久化功能,即文件存儲、 haredPreference 存儲以及數據庫存儲。當然,除了這三種方式之外,你還可以將數據保存在手機的 SD 卡中,不過使用文件、 SharedPreference 或 數據庫來保存數據會相對更簡單一些,而且比起將數據保存在 SD 卡中會更加的安全。

一、文件存儲

文件存儲是 Android 中最基本的一種數據存儲方式,它不對存儲的內容進行任何的格式化處理,所有數據都是原封不動地保存到文件當中的,因而它比較適合用於存儲一些簡單的文本數據或二進制數據。如果w我們想使用文件存儲的方式來保存一些較爲複雜的文本數據,就需要定義一套自己的格式規範,這樣方便於之後將數據從文件中重新解析出來。

1、將數據存儲到文件中

Context 類中提供了一個 openFileOutput() 方法,可以用於將數據存儲到指定的文件中。這個方法接收兩個參數:
- 第一個參數是文件名,在文件創建的時候使用的就是這個名稱。
注意這裏指定的文件名不可以包含路徑,因爲所有的文件都是默認存儲到 /data/data//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//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開源項目“愛閱”。“愛閱”專注於收集優質的文章、站點、教程,與大家共分享。下面是“愛閱”的效果圖:


聯繫方式:

簡書:WillFlow
CSDN:WillFlow
微信公衆號:WillFlow

微信公衆號:WillFlow

發佈了81 篇原創文章 · 獲贊 21 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章