Android 11+ 下将应用专属目录缓存PDF保存至共享存储(Java)
Android 11+ 下将应用专属缓存PDF保存到共享存储的实现
一、权限准备
针对Android 11及以上版本,操作共享存储无需申请WRITE_EXTERNAL_STORAGE权限,仅需在AndroidManifest.xml中声明必要的读取权限(针对Android 13+的媒体类型):
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <!-- Android 13+ 媒体类型权限,PDF等文档无需额外权限 --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
二、获取应用专属缓存中的PDF文件
先拿到应用缓存目录下的源PDF文件对象:
// 内部缓存目录,仅当前应用可访问 File sourcePdf = new File(getCacheDir(), "cached_file.pdf"); // 或外部缓存目录(需确保外部存储可用) // File sourcePdf = new File(getExternalCacheDir(), "cached_file.pdf"); if (!sourcePdf.exists() || !sourcePdf.isFile()) { // 处理文件不存在或不是文件的情况 return; }
三、方案1:通过MediaStore保存到公共文档目录(推荐)
使用MediaStore API是Android 11+操作共享存储的标准方式,以下代码将PDF保存到系统Download目录的子文件夹中:
String targetFileName = "saved_pdf_file.pdf"; ContentValues contentValues = new ContentValues(); // 设置文件信息 contentValues.put(MediaStore.Downloads.DISPLAY_NAME, targetFileName); contentValues.put(MediaStore.Downloads.MIME_TYPE, "application/pdf"); // 指定在Download目录下的子目录,系统会自动创建 contentValues.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + "/MyAppPDFs"); ContentResolver resolver = getContentResolver(); Uri targetUri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues); if (targetUri == null) { // 插入文件记录失败,处理错误 return; } // 复制源文件内容到共享存储 try (InputStream inputStream = new FileInputStream(sourcePdf); OutputStream outputStream = resolver.openOutputStream(targetUri)) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } // 文件保存成功,可提示用户 } catch (IOException e) { e.printStackTrace(); // 复制失败,删除已创建的空文件记录 resolver.delete(targetUri, null, null); }
四、方案2:用SAF让用户选择保存位置
如果需要让用户自定义保存路径,使用存储访问框架(SAF):
// 注册Activity结果回调(AndroidX推荐方式) ActivityResultLauncher<Intent> savePdfLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), result -> { if (result.getResultCode() == RESULT_OK && result.getData() != null) { Uri selectedUri = result.getData().getData(); if (selectedUri != null) { try (InputStream inputStream = new FileInputStream(sourcePdf); OutputStream outputStream = getContentResolver().openOutputStream(selectedUri)) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } // 保存成功 } catch (IOException e) { e.printStackTrace(); } } } } ); // 启动SAF选择保存位置 Intent saveIntent = new Intent(Intent.ACTION_CREATE_DOCUMENT); saveIntent.addCategory(Intent.CATEGORY_OPENABLE); saveIntent.setType("application/pdf"); saveIntent.putExtra(Intent.EXTRA_TITLE, "saved_pdf_file.pdf"); savePdfLauncher.launch(saveIntent);
五、关键注意事项
- 应用专属目录的文件为私有资源,复制到共享存储后其他应用可访问
- 禁止使用
Environment.getExternalStorageDirectory()等废弃API,Android 11+已限制直接访问根目录 - 仅在批量操作非媒体/文档文件时,才需要申请
MANAGE_EXTERNAL_STORAGE权限,该权限需用户手动在设置中开启,不推荐常规场景使用 - 复制文件时必须处理IO异常,避免残留空文件记录
内容的提问来源于stack exchange,提问作者T LABS
相关产品推荐
相关产品推荐

