目录
本文介绍Android的文件存储方式-在存储卡上读写文件,包括:公有存储空间与私有存储空间有什么区别、如何利用存储卡读写文本文件、如何利用存储卡读写图片文件、如何在App运行的时候动态申请权限等。
私有存储空间与公共存储空间
为了更规范地管理手机存储空间,Android从7. 0开始将存储卡划分为私有存储和公共存储两大部分,也就是分区存储方式,系统给每个App都分配了默认的私有存储空间。App在私有空间上读写文件无须任何授权,但是若想在公共空间读写文件,则要在AndroidManifest.xml里面添加下述的权限配置。
<--存储卡读写-->
<uses-permission android:name="android.permission, WRITE_EXTERNAL STORAGE" />
<uses-permission android:name="android.permission, READ_EXTERNAL_STORAGE" />
但是即使App声明了完整的存储卡操作权限,系统仍然默认禁止该App访问公共空间。打开手机的系统设置界面,进入到具体应用的管理页面,会发现该应用的存储访问权限被禁止了。
当然禁止访问只是不让访问存储卡的公共空间,App自身的私有空间依旧可以正常读写。这缘于Android把存储卡分成了两块区域,一块是所有应用均可访问的公共空间,另一块是只有应用自己才可访问的专享空间。虽然Android给每个应用都分配了单独的安装目录,但是安装目录的空间很紧张,所以Android在存储卡的“AIndroid/data”目录下给每个应用又单独建了一个文件目录,用来保存应用自己需要处理的临时文件。这个目录只有当前应用才能够读写文件,其他应用是不允许读写的。由于私有空间本身已经加了访问权限控制,因此它不受系统禁止访问的影响,应用操作自己的文件目录自然不成问题。因为私有的文件目录只有属主应用才能访问,所以一旦属主应用被卸载,那么对应的目录也会被删掉。
既然存储卡分为公共空间和私有空间两部分,它们的空间路径获取方法自然也就有所不同。若想获取公共空间的存储路径,调用的是Environment.getExternalStoragePublicDirectory方法;若想获取应用私有空间的存储路径,调用的是getExternalFilesDir方法。下面是分别获取两个空间路径的代码例子:
// Android7.0之后默认禁止访问公共存储目录
// 获取系统的公共存储路径
String publicPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString();
// 获取当前App的私有存储路径
String privatePath = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString();
boolean isLegacy = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// Android10的存储空间默认采取分区方式,此处判断是传统方式还是分区方式
isLegacy = Environment.isExternalStorageLegacy();
}
String desc = "系统的公共存储路径位于" + publicPath +
"\n\n当前App的私有存储路径位于" + privatePath +
"\n\nAndroid7.0之后默认禁止访问公共存储目录" +
"\n\n当前App的存储空间采取" + (isLegacy?"传统方式":"分区方式");
tv_path.setText(desc);
该例子运行之后获得的路径信息如图所示,可见应用的私有空间路径位于“存储卡根目录/Android/data/应用包名/files/Download”这个目录中。
在存储卡上读写文件
文本文件的读写借助于文件IO流FileOutputStream和FileInputStream.其中,FileOutputStream用于写文件,FileInputStream用于读文件,它们读写文件的代码例子如下:
// 把字符串保存到指定路径的文本文件
public static void saveText(String path, String txt) {
// 根据指定的文件路径构建文件输出流对象
try (FileOutputStream fos = new FileOutputStream(path)) {
fos.write(txt.getBytes()); // 把字符串写入文件输出流
} catch (Exception e) {
e.printStackTrace();
}
}
// 从指定路径的文本文件中读取内容字符串
public stati