You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android应用读写服务器PDF合规方案咨询(适配Scoped Storage)

针对Scoped Storage的PDF存储与打开最佳实践

你的核心问题是在Android 10+版本中,直接申请WRITE_EXTERNAL_STORAGE权限并操作非应用专属的外部存储区域,违反了Scoped Storage规范,导致应用被拒审。以下是适配Scoped Storage的完整解决方案:

1. 清理冗余权限

  • 针对Android 10(API 29)及以上版本,完全移除WRITE_EXTERNAL_STORAGE权限声明。
  • 若需兼容Android 9及以下,可保留权限但添加版本限制,避免影响高版本规则:
    <uses-permission
        android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="28" />
    

2. 选择合规的存储位置

根据你的使用场景,分两种方案:

场景A:PDF仅用于本应用临时打开或内部访问

直接保存到应用专属存储目录,无需任何权限,卸载应用时文件会被自动清理,完全符合Scoped Storage要求:

// 获取应用外部专属文档目录
File appDocDir = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);
if (appDocDir == null) {
    // 处理目录获取失败的异常
    return;
}
File pdfFile = new File(appDocDir, "target_file.pdf");

// 写入文件(复用你原有的写入逻辑)
try (FileOutputStream fos = new FileOutputStream(pdfFile)) {
    fos.write(base64ContentOfMyFile);
    fos.flush();
} catch (IOException e) {
    e.printStackTrace();
}

场景B:需要PDF被用户在文件管理器/其他应用中永久访问

如果要保存到公共Downloads目录,使用MediaStore API(无需WRITE权限):

ContentValues values = new ContentValues();
values.put(MediaStore.Downloads.DISPLAY_NAME, "target_file.pdf");
values.put(MediaStore.Downloads.MIME_TYPE, "application/pdf");
values.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);

ContentResolver resolver = getContentResolver();
Uri mediaUri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values);
if (mediaUri == null) {
    // 处理元数据插入失败的情况
    return;
}

// 写入文件
try (OutputStream outputStream = resolver.openOutputStream(mediaUri)) {
    outputStream.write(base64ContentOfMyFile);
    outputStream.flush();
} catch (IOException e) {
    e.printStackTrace();
    // 写入失败,清理已插入的元数据
    resolver.delete(mediaUri, null, null);
}

3. 优化Intent打开逻辑

你原有的Intent代码存在重复设置setDataAndType、冗余权限flag的问题,优化后版本:

Uri targetUri;
// 场景A:用FileProvider生成共享Uri
targetUri = FileProvider.getUriForFile(getApplicationContext(),
        BuildConfig.APPLICATION_ID + ".provider", pdfFile);

// 场景B:直接使用MediaStore返回的uri
// targetUri = mediaUri;

Intent intentView = new Intent(Intent.ACTION_VIEW);
intentView.setDataAndType(targetUri, "application/pdf");
intentView.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 仅授予读权限即可(打开查看不需要写权限)
intentView.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

Intent intentChooser = Intent.createChooser(intentView, "Open with...");
startActivity(intentChooser);

4. 配置FileProvider(仅场景A需要)

在AndroidManifest.xml中添加FileProvider声明:

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.provider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

在res/xml目录下创建file_paths.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-files-path name="external_documents" path="Documents" />
</paths>

内容的提问来源于stack exchange,提问作者Oza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 04:50:30