Android 11 后的应用数据和文件
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_DOCUMENTintent 操作示例:使用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 设备 来说:
- 存储空间权限更名为文件和媒体权限。
- 只请求
READ_EXTERNAL_STORAGE权限时,相当于只获取了媒体库权限,只能读写媒体库公有目录下的文件、私有目录下的文件和使用SAF获取到的有权限访问的目录。 WRITE_EXTERNAL_STORAGE权限被弃用,要想直接对内部存储空间读写,需要申请所有文件访问权限MANAGE_EXTERNAL_STORAGE
对Android 10 APP(未启用分区存储)、Android 11 设备来说:
- 存储权限更名为文件和媒体权限。
- 只请求
READ_EXTERNAL_STORAGE权限,也相当于请求了Android 11 上的MANAGE_EXTERNAL_STORAGE权限。
对于Android 10 APP(未启用分区存储)、Android 10 及以下设备来说:权限处理同 Android 9 及以下设备。
对于Android 10 APP(启用分区存储)、Android 10 及以上设备来说:
WRITE_EXTERNAL_STORAGE权限无用,要想直接对内部存储空间读写,需关闭分区存储- 请求
READ_EXTERNAL_STORAGE权限时,相当于只获取了媒体库权限,只能读写媒体库公有目录下的文件、私有目录下的文件和使用SAF获取到的有权限访问的目录
Android 11 后的应用数据和文件的更多相关文章
- Android数据存储-文件操作
一.预备知识 1.Android中的MVC设计模式 MVC (Model-View-Controller):M是指逻辑模型,V是指视图模型,C则是控制器.一个逻辑模型可以对于多种视图模型,比如一批统计 ...
- android 数据存储----文件方式 总结
在android中的文件放在不同位置,它们的读取方式也有一些不同. 本文对android中对资源文件的读取.数据区文件的读取.SD卡文件的读取及RandomAccessFile的方式和方法进行了整理. ...
- unity Android 打包后读取 xml 文件
原地址:http://www.cnblogs.com/wuzhang/p/wuzhang20140731.html 问题: 前天在做东西的过程中发现了一个让人很纠结的问题,为什么Unity 程序 ...
- 解决VS2015安装Android SDK 后文件不全及更新问题
近日安装VS2015专业版后.想进行Android开发,就新建了一个Blank app 结果报[值不能为空 null 参数名:path1] 1:首先检查工具 xamarin 工具那设置的SDK路径对不 ...
- 解决更新到os x10.11后openssl头文件无法找到的问题
os x从10.10更新到10.11后,原有代码编译报错,#include <openssl/ssl.h>等头文件无法找到: "openssl/ssl.h: No such fi ...
- 下载额外数据文件失败 以下软件包要求安装后下载附加数据,但其数据无法下载或无法处理 ttf-mscorefonts-installer
故障显示: 一些软件包的数据文件无法下载 以下软件包要求安装后下载附加数据,但其数据无法下载或无法处理. ttf-mscorefonts-installer 这是一个永久错误,系统中的这些软件包将无法 ...
- android基础知识13:AndroidManifest.xml文件解析
注:本文转载于:http://blog.csdn.net/xianming01/article/details/7526987 AndroidManifest.xml文件解析. 1.重要性 Andro ...
- Activity 事件以及如何得到新打开Activity关闭后返回的数据
1: package com.example.activity_basic; 2: 3: import android.os.Bundle; 4: import android.app.Activ ...
- Oracle 数据泵文件
数据泵文件 expdp介绍 EXPDP命令行选项1. ATTACH该选项用于在客户会话与已存在导出作用之间建立关联.语法如下ATTACH=[schema_name.]job_nameSchema_na ...
- Android中的5种数据存储方式
本文转自 http://hi.baidu.com/maguowei/blog/item/7aca46c25574a33ae5dd3ba4.htmlAndroid数据存储Android提供了5种方式存 ...
随机推荐
- 2021-7-29 MySql多表查询详解
多表连接 左连接:返回第一张表的所有数据项然后拼接第二张表(左表全有,右表对应左表才有) 右连接:返回第二张表的所有数据项然后拼接第一张表(右表全有,左表对应右表才有) 内连接:返回两张表数据相等的数 ...
- python连接数据库及查询包含中文错误解决方法
使用MySQLdb库来连接数据库 import MySQLdb conn = MySQLdb.connect(host='127.0.0.1', user='root', passwd='', por ...
- redis 中的 set
set是String中的无序集合 底层是 是 value为null 的hash表 时间复杂化是o(1): sadd k1 v1 v2 v3 set中添加数据 smembers k1 取出set ...
- redis的一些简单操作(针对key)
redis默认16个数据,默认使用0号 select为切换数据库的关键字 select 1 切换数据库 设置值 set k1 lucy key为 k1 value 为 lucy 查看全 ...
- rpm安装21c单实例数据库
linux 7.6 使用rpm安装21c单实例数据库 一.基础环境配置 1.1 关闭防火墙 systemctl stop firewalld systemctl disable firewalld s ...
- 你们眼睛干涩,胀痛吗?C# WPF 久坐提醒桌面小程序
目录 说明 设置提醒时间,及休息时间 久坐提醒倒计时 休息提醒倒计时 休息到计时 代码说明 主窗体设置 工作到计时 休息倒计时 源码 久坐提醒桌面小程序: 干这行职业病比较多,之前用爱丽(即:玻璃酸钠 ...
- 记通过mysql数据库成功入侵到服务器内部的渗透实战
1,在成功进入mysql到数据库之后,可以使用select user();命令,查看当前的用户,如果为root,则可以执行下一步操作.不为root,则需要通过其他方法提权. 2, 使用show var ...
- 利用pytorch自定义CNN网络(一):torchvision工具箱
本文是利用pytorch自定义CNN网络系列的第一篇,主要介绍 torchvision工具箱及其使用,关于本系列的全文见这里. 笔者的运行设备与软件:CPU (AMD Ryzen 5 4600U) + ...
- 2023-09-03:用go编写。给你一个 n 个节点的无向无根树,节点编号从 0 到 n - 1 给你整数 n 和一个长度为 n - 1 的二维整数数组 edges , 其中 edges[i] =
2023-09-03:用go语言编写.给你一个 n 个节点的无向无根树,节点编号从 0 到 n - 1 给你整数 n 和一个长度为 n - 1 的二维整数数组 edges , 其中 edges[i] ...
- Java 21新特性:Sequenced Collections(有序集合)
在JDK 21中,Sequenced Collections的引入带来了新的接口和方法来简化集合处理.此增强功能旨在解决访问Java中各种集合类型的第一个和最后一个元素需要非统一且麻烦处理场景. 下面 ...