Android 中数据存储介绍

271 阅读6分钟

这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战

文章目录

数据存储介绍

数据存储也称之为数据持久化。表现为将程序处理过程中需要保存的数据存储到硬盘的某个文件中。在 Android 中,数据存储主要区分为:
1、偏好设置
2、IO存储/文件存储
3、SQLite数据库存储
4、网络存储

SharedPreferences:偏好设置

偏好设置通常用于存储用户在使用软件时的个性化设置。存储的数据通常存在 K-V 关系,且数据量较小,数据是私有的(不需要共享给其他应用程序)。

偏好设置的本质是使用 XML 存储数据,存储的文件将存在 /data/data/应用程序包名/shared_prefs/ 文件夹下。

如果在设置-应用程序-当前应用-清除数据,会清楚所有偏好设置的设置项。

【保存数据】
1、通过 Context 对象的 getSharedPreferences(String name,int mode)方法获取 SharedPreferences 对象,其中,第 1 个参数标识存储偏好设置的文件的文件名,不包括扩展名;第 2 个参数表示文件的访问类型,取值为 Context.MODE_PREVATE
2、通过 SharedPreferences 对象的 edit()方法,获取文件的编辑工具(Editor 接口类型)的对象
3、调用 Editor 对象的 put???(String key, ?? value) 系列方法执行编辑,即编辑文件,将数据写入文件内部
4、调用 Editor 对象的 commit() 方法提交写入

【读取数据】
1、通过 Context 对象的 getSharedPreferences(String name, int mode)方法获取 SharedPreferences 对象,其中,第 1 个参数表示存储偏好设置的文件的文件名,不包括扩展名,第 2 个参数表示文件的访问类型,取值为 Context.MODE_PREVATE
2、通过 SharedPreferences 对象的 get???() 系列方法读取数据

举例:通过 SharedPreferences 保存数据

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="15dp">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="请输入用户名" />

    <EditText
        android:id="@+id/et_username"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="请选择性别" />

    <RadioGroup
        android:id="@+id/rg_male"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <RadioButton
            android:id="@+id/rb_male"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="男" />

        <RadioButton
            android:id="@+id/rb_female"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="女" />

    </RadioGroup>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="请输入年龄" />

    <EditText
        android:id="@+id/et_age"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="number" />

    <Button
        android:id="@+id/btn_submit"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="提交" />

</LinearLayout>

MainActivity

public class MainActivity extends AppCompatActivity {
    private EditText etUserName;
    private RadioButton rbGenderMale;
    private RadioButton rbGenderFeMale;
    private EditText etAge;
    private Button btnSubmit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        setListeners();
        showData();
    }

    private void initView() {
        etUserName = findViewById(R.id.et_username);
        rbGenderMale = findViewById(R.id.rb_male);
        rbGenderFeMale = findViewById(R.id.rb_female);
        etAge = findViewById(R.id.et_age);
        btnSubmit = findViewById(R.id.btn_submit);
    }

    private void setListeners() {
        btnSubmit.setOnClickListener(view -> {
            String username = etUserName.getText().toString().trim();
            String gender = rbGenderMale.isChecked() ? "男" : "女";
            int age = Integer.parseInt(etAge.getText().toString());
            //执行保存
            String name = "user-info";//保存偏好设置的文件的文件名,不需要指定扩展名
            int mode = Context.MODE_PRIVATE;//文件的访问模式
            SharedPreferences sharedPreferences = getSharedPreferences(name, mode);
            SharedPreferences.Editor editor = sharedPreferences.edit();

            editor.putString("_username", username);
            editor.putString("_gender", gender);
            editor.putInt("_age", age);
            editor.commit();

            Toast.makeText(MainActivity.this, "保存成功", Toast.LENGTH_SHORT).show();
        });
    }

    private void showData() {
        //显示偏好设置中的数据
        String name = "user-info";
        int mode = Context.MODE_PRIVATE;
        SharedPreferences sharedPreferences = getSharedPreferences(name, mode);
        String username = sharedPreferences.getString("_username", null);
        String gender = sharedPreferences.getString("_gender", null);
        int age = sharedPreferences.getInt("_age", 0);

        etUserName.setText(username);
        if ("女".equals(gender)) {
            rbGenderFeMale.setChecked(true);
        } else {
            rbGenderMale.setChecked(true);
        }
        etAge.setText(age == 0 ? "" : (age + ""));
    }
}

我们把程序运行到手机上
在这里插入图片描述
可以在 Android Studio 查看 Device File Explorer ,选择手机
在这里插入图片描述
查看 user-info.xml 的文件内容
在这里插入图片描述

I/O存储/文件存储

如果需要存储的数据表现为独立的文件,则需要使用 IO 方式存储,例如下载得到的图片、歌曲等,或软件运行过程中产生的日志文件等。

使用 IO 存储时,如果是纯问比文件(.txt 文件、.xml 文件、.log 文件)这些文件内部的数据一共是不被频繁读取和解析的。

【存储分类】
IO 存储根据存储的文件位置、是否需要共享给其他应用程序、数据的时效性,可以分为:
1、内部存储
2、外部存储
3、内部缓存存储
4、外部缓存存储

【内部存储】
可以通过 openFileInput(String name)方法获取 FileInputStream 对象,可以通过 openFileOoutput(String name, int mode) 方法获取 FileInputStream 对象。

内部存储的文件都是私有的,所以在设置访问模式时始终使用 MODE_PRIVATE。

内部存储的文件将保存在 /data/data/应用程序报名/files 文件夹。

内部存储的文件也可以通过系统的设置功能进行清除。

与内部存储相关的方法还有:
String[] fileList():获取文件列表
boolean deleteFile(String name):删除文件
File getDir(String name,int mode):获取某个自定义的文件夹的 File 对象,如果自定义文件夹,系统会默认添加 app_ 做位前缀,例如指定的文件夹名称是 logs 时,实际表现为 app_logs,不过开发者可以无视前缀,获取文件夹对象时始终使用 logs 即可。

【外部存储】
外部存储的义件是存储在 sdcard 下的,这些文件都是公有的。
外部存储是不可靠的,因为外部存储本身并不是始终可用的,并且,保存在外部存储中的文件可能被用户或其它 APP 删除或修改。
外部存储的访问应该允分使用 Enviroment 类来获取相关文件夹对象等。实现外部存储依然使用 Java 中的 IO。

【内部缓存存储】
缓存:暂时保存,用于存储有效性特征的数据或文件。
通过调用File getCacheDir()方法,可以获取内部缓存的文件夹的 File 对象,然后再创建出缓存文件的 File 对象,最终创建 FileInputstream / FileoutputStrearm实现文件的读写。
内部缓存将由 Android 系统进行管理和维护,会将该文件夹的容量控制在一定池围之内。
内部缓存也是应用程序私有的,即其它应用程序无法访问。

【外部缓存存储】
与内部缓存不同,外部缓存对应的存储目录是在 sdcard 下的。

通过调用File getExternalcacheDir()方法,可以获取外部缓存的文件夹的 File 对象,然后再创建出缓存文件的 File 对象,最终创建 FileInputstream/Fileoutputstream 实现文件的读写。

外部缓存是公有的。外部缓存与一般外部存储一样,都是不可靠的。

外部缓存不由 Android 系统进行管理和维护,但是,开发者应该制定一系列的逻辑进行管理,例如定期清除,或达到一定容量后清除等。

栗子

这里写图片描述

xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="20dp">


        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="请输入你的个性签名" />

        <EditText
            android:id="@+id/et_sign"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
        <Button
            android:id="@+id/btn_submit"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="提交"/>

</LinearLayout>

MainActivity

public class MainActivity extends AppCompatActivity {
    private EditText et_sign;
    private Button btnSubmit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        setListeners();
        loadData();
    }

    private void loadData() {
        FileInputStream fis = null;
        InputStreamReader isr = null;
        BufferedReader br = null;
        StringBuffer content = new StringBuffer();

        try {
            fis = openFileInput("sign.txt");
            isr = new InputStreamReader(fis);
            br = new BufferedReader(isr);


            String line;
            while ((line = br.readLine()) != null) {
                content.append(line);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        et_sign.setText(content);
    }

    private void setListeners() {
        btnSubmit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String content = et_sign.getText().toString();
                FileOutputStream fos = null;

                try {
                    fos = openFileOutput("sign.txt", MODE_PRIVATE);
                    //getBytes():将一个字符串转化为一个字节数组byte[]的方法
                    byte[] bytes = content.getBytes();
                    fos.write(bytes);
                    fos.flush();
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (fos != null) {
                        try {
                            fos.close();
                            fos = null;//让它被虚拟机视为垃圾
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }

                Toast.makeText(MainActivity.this, "保存成功", Toast.LENGTH_SHORT).show();
            }
        });
    }

    private void initView() {
        et_sign = findViewById(R.id.et_sign);
        btnSubmit = findViewById(R.id.btn_submit);
    }
}

栗子

xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="20dp">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="请输入你的个性签名" />

    <EditText
        android:id="@+id/et_sign"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/btn_submit"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="提交" />

</LinearLayout>

MainActivity

public class MainActivity extends AppCompatActivity {
    private EditText et_sign;
    private Button btnSubmit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        setListeners();
        loadData();
    }

    private void initView() {
        et_sign = findViewById(R.id.et_sign);
        btnSubmit = findViewById(R.id.btn_submit);
    }

    private void setListeners() {
        btnSubmit.setOnClickListener(view -> {
            String content = et_sign.getText().toString();
            FileOutputStream fos = null;

            try {
                fos = openFileOutput("sign.txt", MODE_PRIVATE);
                //getBytes():将一个字符串转化为一个字节数组byte[]的方法
                byte[] bytes = content.getBytes();
                fos.write(bytes);
                fos.flush();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (fos != null) {
                    try {
                        fos.close();
                        fos = null;//让它被虚拟机视为垃圾
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

            Toast.makeText(MainActivity.this, "保存成功", Toast.LENGTH_SHORT).show();
        });
    }

    private void loadData() {
        FileInputStream fis = null;
        InputStreamReader isr = null;
        BufferedReader br = null;
        StringBuffer content = new StringBuffer();

        try {
            fis = openFileInput("sign.txt");
            isr = new InputStreamReader(fis);
            br = new BufferedReader(isr);


            String line;
            while ((line = br.readLine()) != null) {
                content.append(line);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        et_sign.setText(content);
    }
}

运行程序到手机上
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
以上这些内容是上课时讲的,我的博客中还有一篇关于数据存储的文章,是根据《第一行代码 第三版》写的,对于数据存储写的更全面,还有 kotlin 版本代码。👉传送门