Android应用数据的保存方式有四种,分别是应用专属存储空间共享存储偏好设置数据库

应用专属存储空间

应用专属存储空间:存放应用专属文件,主要包括两个空间,卸载后移除

  • 内部存储空间:位于系统内部,通常情况下其他应用无法访问,空间较小,写入前应查询可用空间

    • 内部持久文件目录:/data/user/0/com.xxx/files

      • 获取方法:context.getFilesDir()

      • 可使用java.io.File创建和访问文件

      • 查看文件列表

        Array<String> files = context.fileList();
      • 流操作:

        • 使用信息流存储文件
        String filename = "myfile";
        String fileContents = "Hello world!";
        try (FileOutputStream fos = context.openFileOutput(filename, Context.MODE_PRIVATE)) {
        fos.write(fileContents.toByteArray());
        }
        • 使用信息流读取文件
        FileInputStream fis = context.openFileInput(filename);
        InputStreamReader inputStreamReader =
        new InputStreamReader(fis, StandardCharsets.UTF_8);
        StringBuilder stringBuilder = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(inputStreamReader)) {
        String line = reader.readLine();
        while (line != null) {
        stringBuilder.append(line).append('\n');
        line = reader.readLine();
        }
        } catch (IOException e) {
        // Error occurred when opening raw file for reading.
        } finally {
        String contents = stringBuilder.toString();
        }
    • 内部缓存文件目录:/data/user/0/com.xxx/cache

      • 获取方法:context.getCacheDir()

      • 可使用java.io.File访问文件

      • 创建文件:

        File.createTempFile(filename, null, context.getCacheDir());
      • 移除文件:

        • 对缓存文件:cacheFile.delete();
        • 对应用上下文:context.deleteFile(cacheFileName);
        • 若系统空间不足,缓存文件有被系统直接删除的可能
  • 外部存储空间:通常指的是用户使用文件管理器可直接管理的那部分空间,也包含使用SD卡拓展的空间。

    • 物理卷

      • 通常情况下,Android手机的外部存储空间总是有一个物理卷,所以一般不用进行可用性验证。验证方法:

        Environment.getExternalStorageState()		//如果返回的状态为 Environment.MEDIA_MOUNTED,则可以读写
      • 由于外部存储空间的物理卷可能不止一个(插入SD卡的情况),需要选择用于应用专属存储空间的物理卷。一般情况下,统一文件写入主外部存储卷。获取主外部存储卷路径的方法:

        File[] externalStorageVolumes = ContextCompat.getExternalFilesDirs(getApplicationContext(), null);	//物理卷数组
        File primaryExternalStorage = externalStorageVolumes[0];
    • 可使用java.io.File创建和访问文件

    • 持久文件目录:/storage/emulated/0/Android/data/com.xxx/files

      获取方法:

      context.getExternalFilesDir(null);  //null 可由 android.os.Environment 下的 预定义子目录名称进行替换,以为保存特定媒体文件进行分类
    • 缓存文件目录:/storage/emulated/0/Android/data/com.xxx/cache

      • 获取方法:context.getExternalCacheDir()
      • 删除方法:externalCacheFile.delete();

共享存储

共享存储:包括媒体、文档和其他文件

  • 媒体:MediaStore

    • 分区存储

      • 说明:以Android 10(targetApi = 29)以及更高版本为目标的应用默认情况下仅有对外部存储空间分区访问权限,应用只能访问外部存储空间私有目录/storage/emulated/0/Android/data/com.xxx/files)和自己创建的文件,其他文件无法直接访问。

      • 使用分区存储后,访问外部存储空间公有目录的方法:

        • 使用MediaStore提供的API创建和访问图片、视频、音频资源

          • 示例1:将bitmap图片保存到公有目录Pictures文件夹下

            Bitmap bitmap =  ((BitmapDrawable) img.getDrawable()).getBitmap();
            ContentValues contentValues = new ContentValues();
            contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, System.currentTimeMillis());
            contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
            //指定Pictures文件夹下的二级文件夹名称
            String path = String.format("%s/GalleryApplication/", Environment.DIRECTORY_PICTURES); contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, path); Uri uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
            if(uri != null) {
            try {
            OutputStream outputStream = contentResolver.openOutputStream(uri);
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
            outputStream.close();
            Toast.makeText(MainActivity.this, "添加图片成功", Toast.LENGTH_SHORT).show();
            } catch (IOException e) {
            }
            }
          • 示例2:获取公有目录Pictures文件夹下的图片

            Cursor cursor = contentResolver.query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            null,
            null,
            null,
            MediaStore.MediaColumns.DATE_ADDED + " desc"
            );
            if (cursor != null) {
            while (cursor.moveToNext()) {
            long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID));
            String displayName = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME));
            Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);
            Log.d("wangxiang", "displayName: " + displayName); //文件名
            Log.d("wangxiang", "uri: " + uri); //uri
            }
            cursor.close();
            }
            • 注意:在没有获取读取存储空间的权限READ_EXTERNAL_STORAGE的情况下,只能获取到应用自己创建的图片
        • 使用MediaStore提供的API下载文件到Download目录

        • 使用SAF(存储访问框架)创建和访问其它任意类型的资源

  • 文档和其他文件(媒体文件亦可):使用存储访问框架

    - 创建新文件:使用[`ACTION_CREATE_DOCUMENT`](https://developer.android.com/reference/android/content/Intent?hl=zh-cn#ACTION_CREATE_DOCUMENT) intent 操作
    
         - 示例:另存bitmap图片到手机内存
    
           ```java
    Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("image/*");
    intent.putExtra(Intent.EXTRA_TITLE, System.currentTimeMillis() + ".png");
    startActivityForResult(intent, 666); //startActivityForResult回调时调用
    Uri uri = data.getData();
    try {
    OutputStream outputStream = getContentResolver().openOutputStream(uri); //只选中了目录,数据为空
    Bitmap bitmap = ((BitmapDrawable) img.getDrawable()).getBitmap();
    bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
    outputStream.close();
    Toast.makeText(MainActivity.this, "另存图片成功", Toast.LENGTH_SHORT).show();
    } catch (IOException e) {
    Log.e("wangxiang", "Exception: " + e.getMessage());
    }
    ```
    • 打开文件:ACTION_OPEN_DOCUMENT intent 操作

      • 示例:使用SAF选择图片并打开

        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("image/*");
        startActivityForResult(intent, 777); //startActivityForResult回调时调用
        Uri uri = data.getData();
        Glide.with(this).load(uri).into(img);

偏好设置 - 未完待续

数据库 - 未完待续

总结

Android文件操作一共可以分多少种?

不管分多少种,在本地环境下,除应用私有目录外,Android 11 以后只能获取到uri,逐渐地对文件的操作也就转化成对uri的操作。

对获取到的uri,如何进行文件操作?

使用uri获取输出流outputstream,将其转化为输入流inputstream,再将文件保存至私有目录,就可对其进行传统的文件操作。

对获取到的uri,怎么进行文件上传?

可以通过获取到的流,进行操作。

  • 对Android 11 APP 、Android 11 设备 来说:

    1. 存储空间权限更名为文件和媒体权限。
    2. 只请求READ_EXTERNAL_STORAGE权限时,相当于只获取了媒体库权限只能读写媒体库公有目录下的文件、私有目录下的文件和使用SAF获取到的有权限访问的目录
    3. WRITE_EXTERNAL_STORAGE权限被弃用,要想直接对内部存储空间读写,需要申请所有文件访问权限MANAGE_EXTERNAL_STORAGE
  • 对Android 10 APP(未启用分区存储)、Android 11 设备来说:

    1. 存储权限更名为文件和媒体权限。
    2. 只请求READ_EXTERNAL_STORAGE权限,也相当于请求了Android 11 上的 MANAGE_EXTERNAL_STORAGE权限。
  • 对于Android 10 APP(未启用分区存储)、Android 10 及以下设备来说:权限处理同 Android 9 及以下设备。

  • 对于Android 10 APP(启用分区存储)、Android 10 及以上设备来说:

    1. WRITE_EXTERNAL_STORAGE权限无用,要想直接对内部存储空间读写,需关闭分区存储
    2. 请求READ_EXTERNAL_STORAGE权限时,相当于只获取了媒体库权限只能读写媒体库公有目录下的文件、私有目录下的文件和使用SAF获取到的有权限访问的目录

Android 11 后的应用数据和文件的更多相关文章

  1. Android数据存储-文件操作

    一.预备知识 1.Android中的MVC设计模式 MVC (Model-View-Controller):M是指逻辑模型,V是指视图模型,C则是控制器.一个逻辑模型可以对于多种视图模型,比如一批统计 ...

  2. android 数据存储----文件方式 总结

    在android中的文件放在不同位置,它们的读取方式也有一些不同. 本文对android中对资源文件的读取.数据区文件的读取.SD卡文件的读取及RandomAccessFile的方式和方法进行了整理. ...

  3. unity Android 打包后读取 xml 文件

    原地址:http://www.cnblogs.com/wuzhang/p/wuzhang20140731.html 问题:    前天在做东西的过程中发现了一个让人很纠结的问题,为什么Unity 程序 ...

  4. 解决VS2015安装Android SDK 后文件不全及更新问题

    近日安装VS2015专业版后.想进行Android开发,就新建了一个Blank app 结果报[值不能为空 null 参数名:path1] 1:首先检查工具 xamarin 工具那设置的SDK路径对不 ...

  5. 解决更新到os x10.11后openssl头文件无法找到的问题

    os x从10.10更新到10.11后,原有代码编译报错,#include <openssl/ssl.h>等头文件无法找到: "openssl/ssl.h: No such fi ...

  6. 下载额外数据文件失败 以下软件包要求安装后下载附加数据,但其数据无法下载或无法处理 ttf-mscorefonts-installer

    故障显示: 一些软件包的数据文件无法下载 以下软件包要求安装后下载附加数据,但其数据无法下载或无法处理. ttf-mscorefonts-installer 这是一个永久错误,系统中的这些软件包将无法 ...

  7. android基础知识13:AndroidManifest.xml文件解析

    注:本文转载于:http://blog.csdn.net/xianming01/article/details/7526987 AndroidManifest.xml文件解析. 1.重要性 Andro ...

  8. Activity 事件以及如何得到新打开Activity关闭后返回的数据

    1: package com.example.activity_basic; 2:   3: import android.os.Bundle; 4: import android.app.Activ ...

  9. Oracle 数据泵文件

    数据泵文件 expdp介绍 EXPDP命令行选项1. ATTACH该选项用于在客户会话与已存在导出作用之间建立关联.语法如下ATTACH=[schema_name.]job_nameSchema_na ...

  10. Android中的5种数据存储方式

    本文转自  http://hi.baidu.com/maguowei/blog/item/7aca46c25574a33ae5dd3ba4.htmlAndroid数据存储Android提供了5种方式存 ...

随机推荐

  1. Django: 后台常用操作

    指定状态码 return JsonResponse(data, status=201) Djano删除数据库 删除对应数据库后,删除对应文件 删除对应的记录 Django后台管理 创建超级管理员 py ...

  2. 代码随想录算法训练营第三天| LeetCode 242.有效的字母异位词 349. 两个数组的交集 1. 两数之和

    242.有效的字母异位词       卡哥建议: 这道题目,大家可以感受到数组用来做哈希表给我们带来的遍历之处.  题目链接/文章讲解/视频讲解: https://programmercarl.com ...

  3. 清理MySQL的binlog日志

    前言 MySQL的binlog是以二进制形式打印的日志,没设置自动删除的话,时间长了就会占用大量存储空间.删除MySQL的binlog日志有两种方法:自动删除和手动删除. 自动删除 永久生效:修改My ...

  4. Eolink 出席 QECon 大会,引领「AI+API」技术的革新浪潮

    7月28日-29日,第八届 QECon 质量效能大会在北京成功召开.大会聚焦"数生智慧,高质量发展新引擎",深入探讨如何利用数字化和智能化技术推动软件质量的发展,进而为高质量的经济 ...

  5. Ubuntu虚拟机安装以及在Ubuntu上安装pycharm

    一.在VMware上安装Ubuntu操作系统 1.下载Ubuntu镜像文件 下载地址:清华大学开源软件镜像站 | Tsinghua Open Source Mirror 参考文章:Ubuntu系统下载 ...

  6. 为什么创建 Redis 集群时会自动错开主从节点?

    哈喽大家好,我是咸鱼 在<一台服务器上部署 Redis 伪集群>这篇文章中,咸鱼在创建 Redis 集群时并没有明确指定哪个 Redis 实例将担任 master,哪个将担任 slave ...

  7. VulnStack - ATT&CK红队评估实战(一) Writeup

    VulnStack - ATT&CK红队评估实战(一) Writeup VulnStack(一)环境搭建 项目地址 http://vulnstack.qiyuanxuetang.net/vul ...

  8. WPF MVVM之点滴分享

    (第五点:绑定源有修改) 我并不打算长篇累牍的介绍什么是MVVM.我尽量简洁的介绍,并把自己的经验分享给大家. 一.关于MVVM M:Model,数据模型(后台存储数据的类) V:View,视图(大部 ...

  9. 论文精读:带有源标签自适应的半监督域适应(Semi-Supervised Domain Adaptation with Source Label Adaptation)

    Semi-Supervised Domain Adaptation with Source Label Adaptation 具有源标签适应的半监督域适应 原文链接 Abstract 文章指出当前的半 ...

  10. 上位机使用JS SerialPort进行串口通信, 包含开发环境搭建和完整示例代码

    在嵌入式开发中,我们经常需要使用上位机(PC)与一些电路模块进行通信, 用于获取一些传感器的数据,或者发送命令控制相应的电路模块.NodeJS目前支持使用SerialPort模块进行串口通信,本文主要 ...