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. 2023牛客暑期多校训练营4 AFHJL

    比赛链接 A 题解 知识点:KMP,构造. 考虑构造全 \(0,1\) 串,至少有一个可行. 我们只需要考虑到 \(t\) 的border \(t'\) ,即 \(t'+s+t'\) : 当 \(t' ...

  2. 测试与爬虫—抓包神器之Charles

    前言 之前我们讲到过fiddler(https://www.cnblogs.com/zichliang/p/16067941.html),wireshark(https://www.cnblogs.c ...

  3. PoW是什么?

    PoW是什么? 工作量证明(proof of work,PoW)是一种用于确认和验证区块链交易和新区块有效性的共识算法.区块链中常见的工作量证明算法包括比特币的SHA-256.以太坊的Ethash.莱 ...

  4. 浏览器工作原理及V8引擎

    浏览器解析过程 当浏览器加载html资源时,会进行如下的解析过程 遇见 HTML 标记,构建 DOM 树 遇见 style/link 标记调用相应解析器处理CSS标记,并构建出CSS样式树 遇见 sc ...

  5. maxwell数据抓取工具

    前言 maxwell是一款开源MySQL数据抓取工具,可以读取MySQL的binlog,然后转换成json并输出到kafka.redis等消息队列中. bin/maxwell,用于增量抓取 bin/m ...

  6. 【LaTeX】制作 PPT(更新中)

    目录 Beamer 模板 特性 frame 与 slide \pause itemize 中的尖括号 <strat-end> 参考资料 Beamer 模板 PPT 推荐用 Beamer 模 ...

  7. 《数据结构-C语言》单链表

    @ 目录 单链表 结构定义 初始化 建立 清空 求表长 判断是否为空表 取值 查找 插入 删除 销毁 遍历打印 测试 单链表 结构定义 #include <stdio.h> #includ ...

  8. 初识Storm之HelloWorld程序源码

    1. 新建一个Maven项目,pom.xml代码如下: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xs ...

  9. Content Security Policy(CSP)应用及说明

    什么是CSP CSP全称Content Security Policy ,可以直接翻译为内容安全策略,说白了,就是为了页面内容安全而制定的一系列防护策略. 通过CSP所约束的的规责指定可信的内容来源( ...

  10. ClickHouse(15)ClickHouse合并树MergeTree家族表引擎之GraphiteMergeTree详细解析

    GraphiteMergeTree该引擎用来对Graphite数据(图数据)进行瘦身及汇总.对于想使用ClickHouse来存储Graphite数据的开发者来说可能有用. 如果不需要对Graphite ...