1. 程式人生 > 實用技巧 >《第一行程式碼》閱讀筆記(二十)——持久化技術(檔案儲存+SharedPreferences)

《第一行程式碼》閱讀筆記(二十)——持久化技術(檔案儲存+SharedPreferences)

檔案儲存

——第一行程式碼
檔案儲存是Android中最基本的一種資料儲存方式,它不對儲存的內容進行任何的格式化處理,所有資料都是原封不動地儲存到檔案當中的,因而它比較適合用於儲存一些簡單的文字資料或二進位制資料。如果你想使用檔案儲存的方式來儲存一些較為複雜的文字資料,就需要定義一套自己的格式規範,這樣可以方便之後將資料從檔案中重新解析出來。

首先介紹的就是Context類中提供的一個openFileOutput ()方法,可以用於將資料儲存到指定的檔案中。這個方法接收兩個引數,第一個引數是檔名,在檔案建立的時候使用的就是這個名稱,注意這裡指定的檔名不可以包含路徑,因為所有的檔案都是預設儲存到/data/data//files/目錄下的。第二個引數是檔案的操作模式,主要有兩種模式可選,MODE_PRIVATE 和 MODE_APPEND。

其中MODE_PRIVATE是預設的操作模式,就是覆蓋,而MODE_APPEND,就是追加。其實還有模式:MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE,這兩種模式表示允許其他的應用程式對我們程式中的檔案進行讀寫操作,不過由於這兩種模式過於危險,很容易引起應用的安全性漏洞,已在Android 4.2版本中被廢棄。

除了openFileOutput ()這個點,剩下的都是Java中的資料流技術。

直接上Demo。

檔案儲存Demo

需求:使EditText中的內容儲存

第一步:建立一個新專案,新增一個EditText佈局
這個沒啥好說的
第二步:存資料庫

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();
            }
        }
    }

——第一行程式碼
這裡通過openFileOutput()方法能夠得到一個FileOutputStream物件,然後再借助它構建出一個OutputStreamWriter物件,接著再使用OutputStreamWriter構建出一個BufferedWriter物件,這樣你就可以通過BufferedWriter來將文字內容寫人到檔案中了。

第三步:取資料庫

public String load() {
        FileInputStream in = null;
        BufferedReader reader = null;
        StringBuffer content = new StringBuffer();
        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 {
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return content.toString();
    }

基本上與取資料一致,只不過使用了openFileInput函式。

第四步:載入存取方法
在destroy()的時候存,在create()的時候取。

——第一行程式碼
注意,上述程式碼 在對字串進行非空判斷的時候使用了TextUtils.isEmpty()方法,這是一個非常好用的方法,它可以一次性進行兩種空值的判斷。當傳入的字串等於null或者等於空字串的時候,這個方法都會返回true,從而使得我們不需要先單獨判斷這兩種空值再使用邏輯運算子連線起來了。

完整程式碼如下:

package com.firstcode.filepersistencetest;

import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.EditText;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    private EditText edit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        edit = 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();
        }
    }

    @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();
            }
        }
    }

    public String load() {
        FileInputStream in = null;
        BufferedReader reader = null;
        StringBuffer content = new StringBuffer();
        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 {
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return content.toString();
    }
}

SharedPreferences儲存

——第一行程式碼
不同於檔案的儲存方式,SharedPreferences是使用鍵值對的方式來儲存資料的。也就是說,當儲存一條資料的時候,需要給這條資料提供一個對應的鍵,這樣在讀取資料的時候就可以通過這個鍵把相應的值取出來。而且SharedPreferences還支援多種不同的資料型別儲存,如果儲存的資料型別是整型,那麼讀取出來的資料也是整型的;如果儲存的資料是一個字串,那麼讀取出來的資料仍然是字串。

獲得SharedPreferences物件

使用SharedPreferences之前需要SharedPreferences物件,而獲得物件的方法在Android中主要提供了3種方法用於得到SharedPreferences物件。

  1. Context類中的getSharedPreferences ( )方法

此方法接收兩個引數,第一個引數用於指定SharedPreferences檔案的名稱,如果指定的檔案不存在則會建立一個 ,SharedPreferences檔案都是存放在/data/data//shared_ prefs/目錄下的。第二個引數用於指定操作模式,目前只有MODE_ PRIVATE這一種模式可選,它是預設的操作模式,和直接傳入0效果是相同的,表示只有當前的應用程式才可以對這個SharedPreferences檔案進行讀寫。其他幾種操作模式均已被廢棄,MODE WORLD_ READABLE和MODE_ WORLD_ WRITEABLE這兩種模式是在Android 4.2 版本中被廢棄的,MODE MULTI_PROCESS模式是在Android 6.0版本中被廢棄的。

  1. Activity類中的getPreferences()方法

這個方法和Context中的getSharedPreferences ()方法很相似,不過它只接收一個操作模式引數,因為使用這個方法時會自動將當前活動的類名作為SharedPreferences的檔名。

  1. PreferenceManager類中的getDefaultSharedPreferences()方法

這是一個靜態方法,它接收一個Context引數,並自動使用當前應用程式的包名作為字首來命名SharedPreferences檔案。得到了SharedPreferences 物件之後,就可以開始向SharedPreferences檔案中儲存資料了,主要可以分為3步實現。
(1)呼叫SharedPreferences物件的edit()方法來獲取一個SharedPreferences.Editor物件。
(2)向SharedPreferences.Editor物件中新增資料,比如新增一個布林型資料就使用putBoolean()方法,新增一個字串則使用putString()方法,以此類推。
(3)呼叫apply()方法將新增的資料提交,從而完成資料儲存操作。

SharedPreferences存取方法

SharedPreferences.Editor editor = getSharedPreferences("data", 0).edit();
                editor.putString("name", "Tom");
                editor.putInt("age", 28);
                editor.putBoolean("married", false);
                editor.apply();

SharedPreferences preferences = getSharedPreferences("data", 0);
                String name = preferences.getString("name", "");
                int age = preferences.getInt("age", 0);
                boolean married = preferences.getBoolean("married", false);

SharedPreferences 物件中提供了一系列的get方法,用於對儲存的資料進行讀取,每種get方法都對應了SharedPreferences.Editor中的一種put方法,比如讀取一個布林型資料就使用getBoolean()方法,讀取一個字串就使用getString()方法。這些get方法都接收兩個引數,第一個引數是鍵,傳人儲存資料時使用的鍵就可以得到相應的值了;第二個引數是預設值,即表示當傳入的鍵找不到對應的值時會以什麼樣的預設值進行返回。

記住密碼Demo

第一步:在廣播的最佳實踐的基礎上加上CheckBox
雖然是新控制元件,但是沒啥可說的。

 <CheckBox
            android:id="@+id/remember_pass"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

第二步:修改LoginActivity

package com.firstcode.broadcasttestpractice;

import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;

public class LoginActivity extends BaseActivity {

    private EditText accountEdit;
    private EditText passwordEdit;
    private Button btn;
    private SharedPreferences preferences;
    private SharedPreferences.Editor editor;
    private CheckBox rememberPass;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.login_activity);
        preferences = PreferenceManager.getDefaultSharedPreferences(this);
        accountEdit = findViewById(R.id.username_edit);
        passwordEdit = findViewById(R.id.password_edit);
        rememberPass = findViewById(R.id.remember_pass);
        btn = findViewById(R.id.btn);

        boolean isRemember = preferences.getBoolean("remember_password", false);
        if (isRemember) {
            String account = preferences.getString("account", "");
            String password = preferences.getString("password", "");
            accountEdit.setText(account);
            passwordEdit.setText(password);
            rememberPass.setChecked(true);
        }

        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String account = accountEdit.getText().toString();
                String password = passwordEdit.getText().toString();

                if (account.equals("admin") && password.equals("123456")) {
                    editor = preferences.edit();
                    if (rememberPass.isChecked()) {
                        editor.putBoolean("remember_password", true);
                        editor.putString("account", account);
                        editor.putString("password", password);
                    } else {
                        editor.clear();
                    }
                    editor.apply();

                    Intent intent = new Intent(LoginActivity.this, MainActivity.class);
                    startActivity(intent);
                } else {
                    Toast.makeText(LoginActivity.this, "賬號密碼錯誤!", Toast.LENGTH_SHORT).show();
                }

            }
        });

    }

}