1 手机存储
手机存储:
- Android 4.4 之前的版本:设备的机身存储就是内部存储(internal storage),为了弥补内部存储空间的不足而插入外置的 SD 卡,被称为外部存储(external storage);
- Android 4.4 及以上版本:很多中高端机器将自身的机身扩展到了 8G 以上,将机身存储分为内部存储和外部存储两部分。当然,依然可以插入 SD 卡来扩充外部存储空间;
2 私有存储和共享存储
对于 Android 应用而言,将应用的存储分为私有存储和共享存储。私有存储是每个应用专属的存储空间,而共享存储包含用户手动创建或共享给其他应用的文件,如相册、下载等:
- 私有存储/目录:
- 每个应用都有自己的私有目录,应用间无法访问;
- 私有存储空间的数据,在应用卸载后删除;
- 共享存储/目录:
- 除了私有存储空间外,其他的都被认定为共享存储,比如 Downloads、Documents、Pictures、DCIM、Movies、Music 等;
- 共享存储空间的数据管理混乱,应用能够随意的访问共享空间的数据,会导致隐私数据泄漏;
- 共享存储空间中的文件不会随着 APP 的卸载而被删除;
在 Android 10 之前,只要应用程序获得了READ_EXTERNAL_STORAGE 和WRITE_EXTERNAL_STORAGE 的权限,就可以随意的访问和修改共享存储中的文件。从 Android 10 开始,对共享存储的读写权限变得更加严格,开发者可以选择是否使用分区存储,Android 11 中强制使用了分区存储。
分区存储/沙盒存储机制是一种安全机制,它用于限制应用对存储空间的访问权限,确保每个应用程序只能访问其被授权的资源和数据。这种机制通过为每个应用分配独立的存储区域(沙盒)来实现,每个应用都有自己的私有目录(例如 /data/data/<package_name>/)和自己创建的图片、视频、音频文件等,其他应用无法直接访问或修改其文件。
如果需要访问其他应用创建的媒体文件,则需要通过特性的 API(如 MediaStore API 或者 Storage Access Framework) 来访获取访问权限。
3 文件的读写操作
3.1 权限声明
首先在 AndroidManifest.xml 中声明必要的权限:
xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- 如果你的应用需要访问所有文件,你还可以声明MANAGE_EXTERNAL_STORAGE权限,
但这通常需要用户手动在应用的设置页面开启,并且只有特定的应用类型(如文件管理器)才能请求此权限 -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
android:maxSdkVersion="30"
tools:ignore="ScopedStorage" />
Android 11(API 30)及更高的版本中,对文件读写权限的处理发生了一些变化,主要是为了增强用户隐私和数据保护。如果我们的应用仍然需要访问共享存储空间(如 SD 卡)或需要旧版的存储访问权限,我们仍然可以在 Android 11 及更高版本上使用 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE。
MANAGE_EXTERNAL_STORAGE 是 Android 10/API 29 引入的一个特殊的权限,它允许应用访问所有的文件,但是通常只适用于特定的应用类型(如文件管理器),并且需要用户手动在应用设置页开启。对于大多数的应用,不建议使用此权限。
在 Android 11(API 30)及更高的版本中,如果申请了 MANAGE_EXTERNAL_STORAGE 权限,通常不需要再显示申请 WRITE_EXTERNAL_STORAGE 和 READ_EXTERNAL_STORAGE 权限,因为 MANAGE_EXTERNAL_STORAGE 权限已经允许应用全面的访问共享存储空间(包括读写权限)。
3.2 requestLegacyExternalStorage="true"
Android 10(API 29)及 Android 11(API 30)中,为了帮助开发者过渡到分区存储模型,Google 引入了一个临时解决方案,即 requestLegacyExternalStorage="true"。
xml
<application
android:requestLegacyExternalStorage="true"
... >
...
</application>
当 requestLegacyExternalStorage 属性为 true 的时候,表示应用可以继续使用之前版本的文件访问形式,即可以自由的读写共享存储中的文件。 然而,这个属性仅在 Android 10 和 Android 11 中有效,在未来的版本中可能被弃用。
在设置了 android:requestLegacyExternalStorage="true" 后,应用仍然需要申请 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE 权限。
3.3 运行时请求权限
java
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// 对于Android 11(API 级别 30)及以上版本
// 使用MediaStore API访问媒体文件或者使用分区存储的API
if (Environment.isExternalStorageManager()) {
// 应用已获得管理外部存储的权限
// ...
} else {
// 应用未获得管理外部存储的权限
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivityForResult(intent, REQUEST_CODE_MANAGE_EXTERNAL_STORAGE);
}
} else {
// 对于Android 10(API 级别 29),可以使用以下代码请求访问外部存储的权限
boolean hasPermission = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
if (!hasPermission) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_MANAGE_EXTERNAL_STORAGE) {
// 处理权限申请结果
// 注意:这里无法直接获取到用户是否授予了权限,因为系统设置没有返回结果
// 你可能需要再次检查Environment.isExternalStorageManager()来确定权限状态
}
}