Android-文件储存

Android版本与API级别关系对应表

储存权限

安卓的权限大概分三种

  1. 普通权限:只需要在清单文件中注册即可(例如网络权限)
  2. 危险权限:需要在代码中动态申请,以弹系统Dialog的形式进行请求(例如储存权限)
  3. 特殊权限:需要在代码中动态申请,以跳系统Activity的形式进行请求(例如安卓11中的所有文件访问权限)

关于储存权限

  1. anroid 6之前,它是普通权限,在清单文件中注册即可
  2. anroid 6-10之间,它是危险权限,需要以系统Dialog的形式请求
  3. android 11后,它是特殊权限,需要跳转至系统的“所有文件访问权限”进行授权
这不是拿老子当猴耍吗

android 6前的储存权限

很简单,在清单文件中注册即可

1
2
3
4
<!--    读取-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- 写入-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

android 7-9的储存权限

清单权限+Dialog动态申请

清单文件

1
2
3
4
<!--    读取-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- 写入-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

动态申请,使用时调用RequestPermissions()方法即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//  权限动态申请
private void RequestPermissions() {
// 判断android版本是否大于安卓6
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
// 判断是否已经有储存权限(PackageManager.PERMISSION_GRANTED:已授权;PackageManager.PERMISSION_DENIED:未授权)
if (ActivityCompat.checkSelfPermission(this,Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){
Toast.makeText(this,"已经拥有读取和储存权限",Toast.LENGTH_SHORT).show();
}
else{
ActivityCompat.requestPermissions(this,new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
},1);//最后一个参数是请求码
}
}
}
// 回调判断是否赋予了请求的权限
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// 1就是上边的请求码
if (requestCode == 1){
if (ActivityCompat.checkSelfPermission(this,Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){
Toast.makeText(this,"权限请求成功",Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(this,"权限请求失败",Toast.LENGTH_SHORT).show();
}
}
}

android 10的储存权限

清单权限+Dialog动态申请

清单文件,如果不加入android:requestLegacyExternalStorage=”true”,在安卓 10.0 的设备无法正常读写外部存储的文件

1
2
3
4
5
6
7
8
9
10
11
<!--    读取-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- 写入-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<!-- 并在application中加入-->
<application
...
android:requestLegacyExternalStorage="true">
...
</application>

动态申请

1
//  跟android 7-9一样,不赘述

android 11后的储存权限

清单文件+Dialog动态申请+Activity跳转申请

清单文件

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--    读取外部储存-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- 写入外部储存-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- 管理外部储存-->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

<!-- 并在application中加入,使其做到安卓10兼容-->
<application
...
android:requestLegacyExternalStorage="true">
...
</application>

动态申请

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
//  权限动态申请
private void RequestPermissions() {
// 判断是否为安卓11
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
// 判断是否已经有储存管理权限
if (Environment.isExternalStorageManager()){
Toast.makeText(this,"已经拥有储存管理权限",Toast.LENGTH_SHORT).show();
}
else {
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
// 根据包名打开特定应用程序的权限申请
intent.setData(Uri.parse("package:" + this.getPackageName()));
// startActivityForResult主要用来从FirstActivity跳转到SecondActivity
// 然后返回FirstActivity并且获取从SecondActivity传回来的参数
startActivityForResult(intent,1);//1是请求码
}
}
// 判断android版本是否大于安卓6
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
// 判断是否已经有储存权限(PackageManager.PERMISSION_GRANTED:已授权;PackageManager.PERMISSION_DENIED:未授权)
if (ActivityCompat.checkSelfPermission(this,Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){
Toast.makeText(this,"已经拥有读取和储存权限",Toast.LENGTH_SHORT).show();
}
else{
ActivityCompat.requestPermissions(this,new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
},1);//最后一个参数是请求码
}
}
else{
Toast.makeText(this,"版本小于Android 6,无需申请",Toast.LENGTH_SHORT).show();
}
}

@Override
// 回调权限申请结果方法,判断是否赋予了请求的权限
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// 1就是上边的请求码
if (requestCode == 1){
if (ActivityCompat.checkSelfPermission(this,Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){
Toast.makeText(this,"权限请求成功",Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(this,"权限请求失败",Toast.LENGTH_SHORT).show();
}
}
}

@Override
// 进入另一个Activity再返回HomeActivity时回调该方法
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 1 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
if (Environment.isExternalStorageManager()){
Toast.makeText(this,"储存管理权限请求成功",Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(this,"储存管理权限请求失败",Toast.LENGTH_SHORT).show();
}
}
}

内部储存

随应用卸载被删除

data/data/packageName/目录下

  1. /data/data/packageName/shared_prefs
  2. /data/data/packageName/databases
  3. /data/data/packageName/files
  4. /data/data/packageName/cache
1
2
3
4
5
//获取/data/data/packageName/cache
context.getCacheDir();

//获取/data/data/packageName/files
context.getFilesDir();

FileOutputStream(储存数据)

打开输出流

1
2
//  打开输出流。传入文件名,文件保护类型
FileOutputStream fileOutputStream = openFileOutput("fileName.txt",MODE_PRIVATE);

此时会提示捕获异常,根据提示操作即可

1
2
3
4
5
6
7
//  添加异常捕获
try {
// 打开输出流,传入文件名,文件保护类型
FileOutputStream fileOutputStream = openFileOutput("fileName.txt",MODE_PRIVATE);
} catch (FileNotFoundException e) {
e.printStackTrace();
}

将字符串转为字节组并写入内部储存

1
2
3
4
5
6
7
8
9
10
11
//  字符串
String str = "内部储存";
// 添加异常捕获
try {
// 打开输出流,传入文件名,文件保护类型
FileOutputStream fileOutputStream = openFileOutput("fileName.txt",MODE_PRIVATE);
// 它需要添加捕获异常
fileOutputStream.write(str.getBytes());
} catch (IOException e) {
e.printStackTrace();
}

添加finally,并在其中执行关闭输出流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  FileOutputStream fileOutputStream = null;
// 字符串
String str = "内部储存";
try {
// 打开输出流,传入文件名,文件保护类型
fileOutputStream = openFileOutput("fileName.txt",MODE_PRIVATE);
// 它需要添加捕获异常
fileOutputStream.write(str.getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileOutputStream != null){
try {
// 关闭输出流
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

FileInputStream(读取数据)

打开输入流

1
2
3
  FileInputStream fileInputStream = null;
// 打开输入流。传入需要读取的文件名
fileInputStream = openFileInput("fileName.txt");

添加异常捕获

1
2
3
4
5
6
7
  FileInputStream fileInputStream = null;
// 打开输入流。传入需要读取的文件名
try {
fileInputStream = openFileInput("fileName.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}

定义字节组,创建字符串拼接对象

1
2
3
4
5
6
//  定义字节组,1024表示最大储存1024字节,每次读取最多读1024字节
byte[] bytes = new byte[1024];
// 用于字符串拼接
StringBuilder stringBuilder = new StringBuilder();
// 本次读取的文件流的长度
int len;

循环读取文件字节组,并转换为字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  FileInputStream fileInputStream = null;
// 打开输入流。传入需要读取的文件名
try {
fileInputStream = openFileInput("fileName.txt");
// 定义字节组,1024表示最大储存1024字节,每次读取最多读1024字节
byte[] bytes = new byte[1024];
// 字符串拼接
StringBuilder stringBuilder = new StringBuilder();
// 本次读取的文件流的长度
int len;
// 为0说明读完了
while ((len = fileInputStream.read(bytes)) > 0){
// 将读取的字节转换为字符串并添加到stringBuilder
stringBuilder.append(new String(bytes,0,len));
}
Toast.makeText(this,stringBuilder.toString(),Toast.LENGTH_SHORT).show();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileInputStream != null){
try {
// 关闭输入流
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

外部储存

File类中:

  1. getAbsolutePath()获取绝对路径
  2. getPath()获取相对路径
  3. getName()获取文件名
  4. list()获取指定路径下所有文件(夹)名称数组
  5. listFiles()获取指定目录下所有文件(夹)File数组

公有目录

/storage/emulated/0/下的目录

获取公有目录

1
2
3
4
5
//公有目录根目录
Environment.getExternalStorageDirectory();

//通过填入参数,例如Environment.DIRECTORY_DOWNLOADS可以得到download目录
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)

私有目录

私有目录随应用卸载而删除

/storage/emulated/0/Android/data/packageName目录下

  1. 缓存目录: /storage/emulated/0/Android/data/packageName/cache
  2. 文件目录: /storage/emulated/0/Android/data/packageName/files

FileOutputStream(储存数据)

Environment.getExternalStorageDirectory()外部储存根目录
mkdir()只可以创建一个文件夹,mkdirs()可以创建folder1/folder2/folder3一连串的文件夹

1
2
3
4
5
6
7
8
9
10
11
12
13
//  获取外部储存根目录/myFolder路径
File folder = new File(Environment.getExternalStorageDirectory(),"myFolder");
// folder文件夹中的fileName.txt
File file = new File(folder,"fileName.txt");
// 如果不存在
if (!folder.exists()){
// 新建文件夹
folder.mkdirs();
}
// 如果不存在
if(!file.exists()){
file.createNewFile();
}

外部储存使用new FileOutputStream创建输出流,而非打开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  String str = "外部储存";
// 创建输出流变量
FileOutputStream fileOutputStream = null;
try {
// 获取外部储存根目录/文件夹路径作为对象
File path = new File(Environment.getExternalStorageDirectory(),"lxx/gqtx");
// path路径下的文件
File file = new File(path,"test.txt");
// 路径是否存在,不存在则创建路径文件夹
if (!path.exists()){
path.mkdirs();
}
// 文件是否存在,不存在则创建文件
if (!file.exists()){
file.createNewFile();
}
// 创建输出流对象,打开输出流
fileOutputStream = new FileOutputStream(file);
// 写入字节
fileOutputStream.write(str.getBytes());
}catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭输出流
if (fileOutputStream != null){
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

FileInputStream(读取数据)

外部储存使用new FileInputStream创建输入流,而非打开
getAbsolutePath()会将Environment.getExternalStorageDirectory()的File文件对象类型转换为String

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//  创建输入流变量
FileInputStream fileInputStream = null;
// 获取外部储存根目录/文件夹路径作为对象
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "lxx/gqtx","test.txt");
try {
// 创建输入流对象,打开输入流
fileInputStream = new FileInputStream(file);
// 创建字节组
byte[] bytes = new byte[1024];
// 字符串缓冲区
StringBuilder stringBuilder = new StringBuilder();
// 长度
int len;
// 循环读取字符串
while ((len = fileInputStream.read(bytes)) > 0){
// 进入缓冲区
stringBuilder.append(new String(bytes,0,len));
}
// Toast提示
Toast.makeText(this,stringBuilder.toString(),Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}