Android 10+及以下设备无需MANAGE_EXTERNAL_STORAGE的存储权限方案
无需MANAGE_EXTERNAL_STORAGE的视频播放器存储权限适配方案
一、权限配置调整
先把MANAGE_EXTERNAL_STORAGE权限从清单中移除,再根据不同Android版本配置必要权限:
<!-- 全版本通用的媒体读取权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- Android 10及以下需要的写入权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" /> <!-- 仅针对Android 10(API 29)启用旧存储兼容模式,API 30+无需设置 --> android:requestLegacyExternalStorage="true"
二、权限检查与申请逻辑重构
删掉原代码中MANAGE_EXTERNAL_STORAGE相关的逻辑,针对不同版本做适配:
1. 权限检查方法
private boolean checkPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // Android 11+:访问公共媒体目录无需额外权限,直接通过MediaStore操作 return true; } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) { // Android 10:仅需读取权限,写入通过MediaStore完成 return ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; } else { // Android 9及以下:需要同时拥有读写权限 int readResult = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE); int writeResult = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE); return readResult == PackageManager.PERMISSION_GRANTED && writeResult == PackageManager.PERMISSION_GRANTED; } }
2. 权限申请方法
private static final int PERMISSION_REQUEST_CODE = 100; private void requestPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // Android 11+:无需申请权限,直接执行存储操作 performStorageOperations(); } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) { // Android 10:仅申请读取权限 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE); } else { // Android 9及以下:申请读写权限 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE); } }
3. 权限回调处理
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == PERMISSION_REQUEST_CODE) { boolean allGranted = true; for (int result : grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { allGranted = false; break; } } if (allGranted) { performStorageOperations(); } else { Toast.makeText(this, "请授予存储权限以访问视频文件", Toast.LENGTH_SHORT).show(); } } } // 权限通过后执行存储操作的入口方法 private void performStorageOperations() { // 读取视频列表、修改名称、删除、下载等逻辑写在这里 }
三、核心功能实现(基于MediaStore)
Android10+不能直接用File API操作公共目录,必须通过MediaStore完成需求:
1. 读取/storage/Media/Videos目录下的视频列表
private List<String> getVideoList() { List<String> videoPaths = new ArrayList<>(); String[] projection = {MediaStore.Video.Media.DATA}; Cursor cursor = getContentResolver().query( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, projection, null, null, MediaStore.Video.Media.DATE_ADDED + " DESC" ); if (cursor != null) { while (cursor.moveToNext()) { String path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA)); videoPaths.add(path); } cursor.close(); } return videoPaths; }
2. 修改视频名称
private void renameVideo(String oldPath, String newName) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // Android 10+:通过MediaStore更新 ContentValues values = new ContentValues(); values.put(MediaStore.Video.Media.DISPLAY_NAME, newName); String selection = MediaStore.Video.Media.DATA + "=?"; String[] selectionArgs = {oldPath}; getContentResolver().update( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values, selection, selectionArgs ); } else { // Android 9及以下:直接操作文件 File oldFile = new File(oldPath); if (oldFile.exists()) { File newFile = new File(oldFile.getParent(), newName); oldFile.renameTo(newFile); } } }
3. 删除视频
private void deleteVideo(String videoPath) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // Android 10+:通过MediaStore删除 String selection = MediaStore.Video.Media.DATA + "=?"; String[] selectionArgs = {videoPath}; getContentResolver().delete( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, selection, selectionArgs ); } else { // Android 9及以下:直接删除文件 File videoFile = new File(videoPath); if (videoFile.exists()) { videoFile.delete(); } } }
4. 下载视频到/storage/Download目录
private void downloadVideoToDownloadDir(InputStream inputStream, String fileName) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // Android 10+:通过MediaStore写入Download目录 ContentValues values = new ContentValues(); values.put(MediaStore.Downloads.DISPLAY_NAME, fileName); values.put(MediaStore.Downloads.MIME_TYPE, "video/mp4"); // 根据实际视频类型调整 values.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS); Uri uri = getContentResolver().insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values); if (uri != null) { try (OutputStream outputStream = getContentResolver().openOutputStream(uri)) { byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } } catch (IOException e) { e.printStackTrace(); } } } else { // Android 9及以下:直接写入Download目录 File downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); File videoFile = new File(downloadDir, fileName); try (OutputStream outputStream = new FileOutputStream(videoFile)) { byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } } catch (IOException e) { e.printStackTrace(); } } }
四、关键注意事项
- Android 11+:完全不需要申请存储权限,所有操作通过MediaStore完成,符合Google Play权限政策。
- Android 10:启用
requestLegacyExternalStorage="true"后,既可以兼容旧File API,也可以用MediaStore,建议优先迁移到MediaStore适配后续版本。 - 绝对不要使用
MANAGE_EXTERNAL_STORAGE,该权限仅开放给文件管理器等特殊应用,普通视频播放器不符合审核要求。
内容的提问来源于stack exchange,提问作者Ayan Mishra
相关产品推荐
相关产品推荐

