Android 外部存储与内部存储详解

一、前言

在 Android 本地数据存储之 SharePreferences 存储中,我们已经讲了 Sp 是如何保存数据的,并且也提供了一个 Sp 的工具类,今天我们来讲解一下 Android 中本地存储数据的另外一种方式——文件存储,文件存储又分为外部存储和内部存储。

二、外部存储

2.1、概述

不一样的手机,外部存储在手机中的位置可能是不一样的,有些手机是在 storage 文件夹下,有些手机是在 mnt 文件夹下,我们通常用来表示外部存储的话,都会找一个叫做 sdcard 的文件夹,在 mnt 下面我们可以直接找到这个文件夹,但是在 storage 下面会首先看到 emulated 的文件夹,然后 emulated 下面还有一个叫做 0 的文件夹,这个 0 的文件夹才是真实的外部存储的目录,通常这个目录是无法打开的,因为在这一类型的设备中间会形成一个映射,而这个映射文件夹的名字就叫做 sdcard,所以这种情况下我们就会直接去找这个 sdcard 文件夹。

2.2、获取外部存储位置(SDK 29 之前)

在 Android SDK 29 之前,想要查看外部存储的真实目录只需要调用 Environment.getExternalStorageDirectory() 就可以了,但是在 Android SDK 29 之后,这个方法就被废弃了,如下所示,在 SDK 为29 的情况下,getExternalStorageDirectory() 这个方法显示的就是被废弃了。

Android 外部存储与内部存储详解_第1张图片

2.3、获取外部存储私有目录(SDK 29)

之所以 getExternalStorageDirectory() 这个方法被废弃的原因就是,保存在这个目录下的数据即时应用被卸载了,这些数据依然会存在外部存储里面,而这些数据是很有可能成为垃圾数据的,这样对用户是非常不友好的,所以呢谷歌提供了 getExternalFilesDir()getExternalCacheDir() 这两个方法来获取外部存储的私有目录,前一个存放需要长时间保存的数据,后一个就存一些临时数据,它们位于 SDCard/Android/data/包名/files(cache) 下面。我们用一个简单的例子来演示一下。

2.4、外部存储实例演示

这个例子也很简单,就是往这个目录下存一些数据再读出来,具体效果如下所示:


完整代码如下所示:

public class ExternalActivity extends AppCompatActivity {

    private EditText infoEdt;
    private TextView txt;

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

        infoEdt = findViewById(R.id.info_edt);
        txt = findViewById(R.id.textView);
    }

    public void operate(View v) {
        String path = getExternalFilesDir(null).getAbsolutePath() + "/test.txt";
        Log.e("ExternalActivityTag", path);
        switch (v.getId()) {
            case R.id.save_btn:
                File file =  getExternalFilesDir(null);
                try {
                    if (!file.exists()) {
                        file.createNewFile();
                    }
                    FileOutputStream fos = new FileOutputStream(path, true);
                    String str = infoEdt.getText().toString();
                    fos.write(str.getBytes());
                } catch (IOException e) {
                    e.printStackTrace();
                }
                break;
            case R.id.read_btn:
                try {
                    FileInputStream fis = new FileInputStream(path);
                    byte[] b = new byte[1024];
                    int len = fis.read(b);
                    String str2 = new String(b, 0, len);
                    txt.setText(str2);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;
        }
    }
}

打印出来的具体位置如下所示(结合概述所讲内容可以知道这个目录我们可以在映射目录 sdcard 下面找到):
在这里插入图片描述

2.5、注意

利用 getExternalFilesDir()getExternalCacheDir() 这两个方法来获取外部存储的私有目录是不需要任何权限的,但是如果用 Environment.getExternalStorageDirectory() 是需要外部存储的读写权限的,而且在 Android 6.0 之后,只在清单文件中声明是不够的,还需要运行时申请,即动态权限。

三、内部存储

3.1、概述

首先,内部存储不是内存。在 Android studio 中,内部存储可以通过 Device File Explorer 找到,文件夹叫做 data,如果你想将文件存储于内部存储中,那么文件默认只能被你的应用访问到,且一个应用所创建的所有文件都在和应用包名相同的目录下。也就是说应用创建于内部存储的文件,与这个应用是关联起来的。当一个应用卸载之后,内部存储中的这些文件也被删除。

3.2、获取内部存储位置

我们可以通过 getFileDir()getCacheDir() 这两个方法来获取内部存储的目录,它们位于 data/data/包名/files(cache) 下面,我们同样用外部存储的实例来演示一下,只是把数据存到内部存储中,因为实例效果是完全一样的,就不演示了,直接看代码(具体代码写法上跟外部存储有点不一样):

public class InternalActivity extends AppCompatActivity {

    private EditText edt;
    private TextView txt;

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

        edt = findViewById(R.id.editText);
        txt = findViewById(R.id.textView);
    }

    public void operate(View v) {
        File file = new File(getFilesDir(), "getFilesDir.txt");
        switch (v.getId()) {
            case R.id.save_btn:
                try {
                    if (!file.exists()) {
                        file.createNewFile();
                    }
                    FileOutputStream fos = new FileOutputStream(file);
                    fos.write(edt.getText().toString().getBytes());
                    fos.close();
                } catch (Exception e) {

                }
                break;
            case R.id.read_btn:
                try {
                    FileInputStream fis = new FileInputStream(file);
                    byte[] b = new byte[1024];
                    int len = fis.read(b);
                    String str2 = new String(b, 0, len);
                    txt.setText(str2);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;
        }
    }
}

四、外部存储与内部存储对比

这里对比的是私有目录,如下表格所示:

| | 外部存储 | 内部存储 |
|--|--|--|
| 目录获取方式 | getExternalFilesDir() | getFilesDir() |
| 位置 | SDCard/Android/data/包名/files | /data/data/包名/files |

getExternalCacheDir()getCacheDir() 同理。

五、小结

内部存储与外部存储的知识点我们都已经讲完了,那我们在存储数据的时候应该怎么用呢?一般来说我们不会去用Environment.getExternalStorageDirectory() 这个目录的,因为新的手机基本上都是基于 SDK 29 的,所以,如果我们的内存卡不存在或者被移除的情况下,我们就采用内部存储,如果内存卡存在或者手机自带内存卡的情况下我们就把数据存到外部存储的私有目录下。

你可能感兴趣的:(android)