目录

Android 存储权限兼容问题全解析

Android 存储权限兼容问题全解析

一、引言

在 Android 开发中,存储权限是一个常见且重要的权限。不同版本的 Android 系统对于存储权限的管理和处理方式存在差异,这就导致了开发者在处理存储权限时会遇到各种兼容问题。本文将详细介绍 Android 各版本存储权限的变化,以及如何进行兼容处理,并给出具体的代码示例。

二、Android 各版本存储权限变化

2.1 Android 6.0(API 级别 23)之前

在 Android 6.0 之前,应用的权限在安装时一次性授予。只要用户安装了应用,应用就拥有了在 AndroidManifest.xml 中声明的所有权限,无需在运行时再次请求。例如,要使用存储权限,只需在 AndroidManifest.xml 中添加如下代码:

js 复制代码
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> 
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

2.3 Android 10(API 级别 29)及之后

Android 10 引入了分区存储(Scoped Storage)机制,对应用访问外部存储的方式进行了重大改变。应用默认只能访问自己的专属目录(如 Context.getExternalFilesDir() 返回的目录)和媒体文件,访问其他应用的文件需要特殊处理。同时,WRITE_EXTERNAL_STORAGE 权限不再授予普通应用,除非应用在 AndroidManifest.xml 中声明 android:requestLegacyExternalStorage="true" 以使用旧的存储模式。

2.4 Android 11(API 级别 30)及之后

Android 11 进一步加强了分区存储的限制,android:requestLegacyExternalStorage 属性不再起作用,应用必须适应分区存储。不过,Android 11 提供了一些新的 API 来处理文件访问,如 MediaStore API 用于访问媒体文件。

三、兼容处理方案及代码示例

3.1 权限检查与请求

以下是一个在 Android 6.0 及以上版本中请求存储权限的代码示例:

js 复制代码
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

public class MainActivity extends AppCompatActivity {
    private static final int PERMISSION_REQUEST_CODE = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 检查是否已经授予存储权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            // 如果没有授予权限,则请求权限
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                    PERMISSION_REQUEST_CODE);
        } else {
            // 已经授予权限,执行相关操作
            performStorageOperation();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == PERMISSION_REQUEST_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 用户授予了权限,执行相关操作
                performStorageOperation();
            } else {
                // 用户拒绝了权限,给出提示
                Toast.makeText(this, "存储权限被拒绝,无法执行操作", Toast.LENGTH_SHORT).show();
            }
        }
    }

    private void performStorageOperation() {
        // 执行存储相关操作
        Toast.makeText(this, "开始执行存储操作", Toast.LENGTH_SHORT).show();
    }
}

3.2 Android 10 及以上版本的分区存储处理

在 Android 10 及以上版本中,使用分区存储可以通过 Context.getExternalFilesDir() 访问应用的专属目录。以下是一个简单的文件读写示例:

js 复制代码
import android.content.Context;
import android.os.Bundle;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class StorageActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_storage);

        // 获取应用的外部存储专属目录
        File externalFilesDir = getExternalFilesDir(null);
        if (externalFilesDir != null) {
            // 创建一个文件
            File file = new File(externalFilesDir, "test.txt");
            try {
                // 写入文件
                FileOutputStream fos = new FileOutputStream(file);
                String content = "Hello, World!";
                fos.write(content.getBytes());
                fos.close();
                Toast.makeText(this, "文件写入成功", Toast.LENGTH_SHORT).show();
            } catch (IOException e) {
                e.printStackTrace();
                Toast.makeText(this, "文件写入失败", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

3.3 Android 11 及以上版本的媒体文件访问

js 复制代码
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;

public class MediaAccessActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_media_access);

        // 获取 ContentResolver
        ContentResolver contentResolver = getContentResolver();
        // 定义查询的 URI
        Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        // 定义查询的列
        String[] projection = {MediaStore.Images.Media._ID, MediaStore.Images.Media.DISPLAY_NAME};
        // 执行查询
        Cursor cursor = contentResolver.query(uri, projection, null, null, null);
        if (cursor != null) {
            while (cursor.moveToNext()) {
                // 获取图片的 ID 和文件名
                long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID));
                String displayName = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME));
                Toast.makeText(this, "图片 ID: " + id + ", 文件名: " + displayName, Toast.LENGTH_SHORT).show();
            }
            cursor.close();
        }
    }
}

四、总结

在 Android 开发中,处理存储权限兼容问题需要根据不同版本的系统特点进行相应的处理。在 Android 6.0 及以上版本中,要使用运行时权限机制;在 Android 10 及以上版本中,要适应分区存储;在 Android 11 及以上版本中,要使用新的 API 进行文件访问。通过合理的权限管理和文件操作,可以确保应用在不同版本的 Android 系统上都能正常运行。

本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除
相关推荐
_一条咸鱼_2 小时前
深入剖析 Android Hilt 框架的依赖生命周期管理模块(六)
android·kotlin·android jetpack
_一条咸鱼_2 小时前
深入剖析 Android Hilt 的模块配置与初始化模块(五)
android·kotlin·android jetpack
斗锋在干嘛4 小时前
Android里面开子线程的方法
android
yangshuo12814 小时前
WSA(Windows 安卓子系统)过检测教程
android
jiet_h5 小时前
Kotlin 中 集合 Collection 的扩展方法完全指南
android·开发语言·kotlin
吃饭了呀呀呀6 小时前
🐳 《Android》 安卓开发教程 - 自定义 Toast
android·后端
斗锋在干嘛7 小时前
在Android中问AMS 和 PMS的区别和作用
android
雯0609~9 小时前
PHP:将关联数组转换为索引数组的完整示例
android·oracle·php
Wgllss9 小时前
Android提升开发测试效率,程序员应该多干了些什么?
android·架构·android jetpack
兰琛9 小时前
Android免费实现excel文件(简单)转为PDF文件
android·pdf·excel