Android 分区存储

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()来确定权限状态
    }
}
相关推荐
xiangpanf8 小时前
Laravel 10.x重磅升级:五大核心特性解析
android
robotx11 小时前
安卓线程相关
android
消失的旧时光-194311 小时前
Android 面试高频:JSON 文件、大数据存储与断电安全(从原理到工程实践)
android·面试·json
dalancon12 小时前
VSYNC 信号流程分析 (Android 14)
android
dalancon12 小时前
VSYNC 信号完整流程2
android
dalancon12 小时前
SurfaceFlinger 上帧后 releaseBuffer 完整流程分析
android
用户693717500138413 小时前
不卷AI速度,我卷自己的从容——北京程序员手记
android·前端·人工智能
程序员Android14 小时前
Android 刷新一帧流程trace拆解
android
墨狂之逸才14 小时前
解决 Android/Gradle 编译报错:Comparison method violates its general contract!
android
阿明的小蝴蝶15 小时前
记一次Gradle环境的编译问题与解决
android·前端·gradle