Android 存储——文件存储

    Android 数据存储的方式有很多种,其中文件存储就是我们经常会使用的一种方式,在了解文件存储之前,我们先来了解一下我们手机的存储空间。

    手机的存储空间可以分为三个部分:系统分区、程序分区、公共存储空间。

    系统分区:就是手机操作系统所占用的分区,是内存空间目录下的 system 目录,其中系统自带的 apk 就在 system/app 目录下,需要注意的是,手机普通用户对于 system 目录是没有写的权限的。

    程序分区:就是我们自己安装程序所占的分区,是内存空间目录下的 data 目录,安装的 apk 在  data/app 目录下,而 应用所产生的私有数据在 data/data 目录下,需要注意的是,系统应用所产生的私有数据也在该目录下。

    公共存储空间:就是我们用来存储一些公共的资源的时候所占的分区,是内存空间目录下的 storage/sdcard 目录下。

一、项目内文件

    比如当我们需要使用一些数据的时候,我们可以选择直接读写文件,也可以在项目里面使用文件,在项目里面使用文件有两种方式,一个是 res/raw 目录,一个是 main/assets 目录,这两种方式都会在打包时被放到 apk  中。如果这两个目录不存在的话都可以新建,raw 目录直接右键 res 新建一个文件就行,而 assets 这个方法不太好创建,我们右键 app,新建一个 Folder,选择 Assets Folder:

     

1.  读取 raw 目录下的文件资源,我们先在目录下创建一个文件资源 word.txt 写入一些字符:

private String getByRaw(){
        // 获取输入流
        InputStream inputStream = this.getResources().openRawResource(R.raw.word);
        // 使用 reader 一行一行读取,并添加到 StringBuffer 中拼接出来
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        StringBuffer buffer = new StringBuffer();
        String s = null;
        try {
            while ((s = reader.readLine())!=null ){
                buffer.append(s);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 打印出来
        Log.i("raw:",buffer.toString());
        return buffer.toString();
    }

    结果:


    需要注意的是,在 raw 目录下,不论后缀名是否相似,都不能有相同的文件名,要不然会出错,也不能在 raw 目录下继续创建文件夹。

2. 读取assets 目录下文件资源,和 raw 不同的是,assets 文件目录下是可以新建二级目录的,比如我们在 assets 目录下新建一个 word 目录,然后再新建一个 word.txt 文件:

private String getByAssets(){

        StringBuffer buffer = new StringBuffer();
        // 获取输入流
        try {
            // 默认文件目录为 assets 根目录
            InputStream inputStream = getAssets().open("word/word.txt");
            // 使用 reader 一行一行读取,并添加到 StringBuffer 中拼接出来
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String s = null;
            while ((s = reader.readLine())!=null ){
                buffer.append(s);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        Log.i("Assets:",buffer.toString());
        return buffer.toString();
    }

    结果:


二、私有存储空间

    而我们应用的私有数据是在 data/data 目录下的,一般会使用应用程序包名里面的两个文件夹,files 和 cache,看名字大家也能够知道知道这两个文件拿来干嘛的,files 用来读写数据的,cache 用来读写缓存的。

1. files 文件操作

private void saveByFiles(){
        try {
            // 获取输出流,传入文件名和模式
            OutputStream outputStream = this.openFileOutput("word",MODE_PRIVATE);
            String s = "Hello World!";
            byte[] bytes = s.getBytes("utf-8");
            // 写入
            outputStream.write(bytes);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch (UnsupportedEncodingException e){
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    上面我们可以看到,创建输出流的时候会给文件创建模式,这个模式又是什么呢?这个模式有四种:

模式名 说明
MODE_PRIVATE 默认写法,代表私有,而且每次写入文件会覆盖原来的内容
MODE_APPEND 每次写文件不会覆盖原来的内容,而是从最后继续写
MODE_READABLE 使文件能被别的应用读取
MODE_WRITEABLE 使文件能被别的应用写入

    我们说了,这个 data/data 文件目录下都是一些应用的私有数据,为什么还会有模式来允许别的应用来读写, 这个有时候是为了同一公司或个人出品的应用进行数据共享,要使用这个模式来读写首先需要你的应用的签名是一致的,而且在 Manifest 文件中定义的 shareId 也要一致。虽然这种方法能进行进程间文件资源共享,但是是不鼓励这么做的,我们可以通过其它方式进行进程间文件资源共享,比如说数据库、网络、ContentProvider 等。

    我们来打开 files 目录,会看到有一个 word 的文件,打开文件:


    我们会看到我们写入的内容,那我们怎么读取呢?

private String getByFile(){
        StringBuffer buffer = new StringBuffer();
        try {
            // 通过文件名读取
            InputStream inputStream = this.openFileInput("word");
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String s = null;
            while((s = reader.readLine())!=null){
                buffer.append(s);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }
        Log.i("Files:",buffer.toString());
        return buffer.toString();
    }

    这样就可以读取私有文件 file 里的文件内容了。

2. cache 文件操作

private void saveByCache(){
        // 获取 cache 文件
        File cacheFile = this.getCacheDir();
        // 新文件,文件路径为 cache 文件夹的绝对路径里面的 wordCache 文件
        File file = new File(cacheFile.getAbsolutePath() + "/wordCache");
        try {
            FileOutputStream outputStream = new FileOutputStream(file);
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream));
            writer.write("Hello Cache");
            writer.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e){
            e.printStackTrace();
        }
    }

    然后我们打开 data/data 里面项目包名里的 cache 文件目录,发现里面有一个 wordCache 文件,打开该文件:


    然后我们来获取文件内容:

private String getByCache(){
        File cacheFile = this.getCacheDir();
        File file = new File(cacheFile.getAbsolutePath() + "/wordCache");
        StringBuffer buffer = new StringBuffer();
        try {
            FileInputStream inputStream = new FileInputStream(file);
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String s = null;
            while ((s = reader.readLine())!=null){
                buffer.append(s);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e){
            e.printStackTrace();
        }
        Log.i("Cache:",buffer.toString());
        return buffer.toString();
    }

    结果:


    这就是私有缓存空间的一些文件操作,需要注意的是私有缓存会在内存不足时自动清除,有时候会在我们不知道的情况下清除一些数据,如果我们想要避免这种操作,就可以使用规定缓存大小的缓存,大家可以使用 DiskLruCache,这里大家可以看看我的这一篇博客——Android 缓存策略,这里有面有对几种缓存的一个分享。

三、公共存储空间

1. 拓展空间

    上面我们说到,公共存储空间就是 storage/sdcard 目录下的空间。其实,在 sdcard 目录下,有一个 android/data 目录,可以看到下面也是一些项目包名,包名目录下也有 file 和 cache 目录,其实这个是应用程序的扩展空间,因为应用程序的私有空间是有限的,相对来说手机的存储空间是很大的,我们就可以把一些相对来说不是那么敏感的数据放到拓展空间中。

    而且使用拓展空间,不会产生垃圾文件,当应用卸载或者清除缓存和数据时,除了私有空间的 file 和 cache 会被清除以外,拓展程序的 file 和 cache 也会被清除。其次,放在拓展空间的程序文件不会被媒体扫描器扫描,有一定隐私性。而且,Google 也在 API 19 时,使用拓展空间是不需要在 manifest 中声明内存权限的。注意:使用拓展存储空间也是会被自动清理的,所以也可以使用 DiskLruCache 等。

private void getByExternalFile(){
    // 使用拓展文件,需要传入 String 类型的参数
    // null 代表就是 file 目录下
    File file1 = this.getExternalFilesDir(null);
    // "word" 代表是 file/word/ 目录
    File file2 = this.getExternalFilesDir("word");
    // 使用静态常量,代表 file/Music/ 目录,有很多的常量,大家可以去 Environment 源码中看看
    File file3 = this.getExternalFilesDir(Environment.DIRECTORY_MUSIC);
    // 下面的操作和上面是一样的
}
private void getByExternalCache(){
    File file = this.getExternalCacheDir();
}

2. 公共空间

    使用公共空间时的操作方式是和 Java 中的 IO 操作一样的,和上面的文件操作也是类似的,就不做详细说明了:

// 获取公共存储空间的目录
        File sFile = Environment.getExternalStorageDirectory();

    我们怎么在公共空间来隐藏自己的文件呢?1. 将文件名改为 .xxx,2. 将文件根目录添加 .nomedia 文件,这样文件扫描的时候就会把这些文件夹作为隐藏文件而不扫描。

注意:这里就会有一个问题,当用户手机存储只有通过 sd 卡 存储时,即当 sd 卡拔出时,你应用所有使用的资源就全为空了,这个时候应用程序就会出现异常或者影响使用效果,我们就需要判断 sd 卡是否可用来告诉用户。

private boolean isSdCardUsable(){
        String state = Environment.getExternalStorageState();
        if (state == Environment.MEDIA_MOUNTED){
            return true;
        }else{
            return false;
        }
    }
    我们也可以使用广播来通知 SD 卡拔出或者插入,这里大家就可以看一下广播的内容。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值