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

API 27开发中Android Studio无法打开Assets目录PDF求助

解决API27下Assets目录PDF无法打开的问题

嘿,你遇到的这个Assets目录PDF打不开的问题,我之前在API27版本开发的时候也碰到过,核心问题其实是Assets里的文件不能直接用常规File对象访问,再加上Android 7.0之后的Uri权限限制,导致你试了很多方法都没用。我给你一步步梳理解决办法:

1. 先确认Assets文件的正确性

首先得排除基础错误:

  • 检查Assets目录里的PDF文件名大小写是否完全匹配,Android的Assets路径是区分大小写的,比如test.pdf和Test.pdf是两个不同的文件
  • 用代码打印Assets目录的文件列表,确认文件真的被打包进去了:
    try {
        String[] files = getAssets().list(""); // 如果PDF在子目录,比如assets/pdf/,就传"pdf"
        for (String file : files) {
            Log.d("AssetsFiles", "文件:" + file);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    
    看日志里有没有你要打开的PDF文件名,要是没有,说明文件没被正确打包,检查你的项目结构或者build配置。

2. 为什么file.exists()一直返回false?

这是关键误区:Assets里的文件是打包在APK内部的,不属于设备的文件系统,所以你直接用new File("file:///android_asset/xxx.pdf")这种方式创建的File对象,肯定会返回false——因为这个路径在设备的真实文件系统里根本不存在。

必须先把Assets里的PDF复制到应用可访问的存储目录(比如缓存目录、内部存储),然后再访问复制后的文件。

3. 完整的实现步骤

第一步:复制Assets文件到缓存目录

缓存目录不需要申请存储权限,适合临时存放这类文件:

private void copyAssetToCache(String assetFileName) {
    InputStream inputStream = null;
    OutputStream outputStream = null;
    try {
        // 打开Assets里的文件流
        inputStream = getAssets().open(assetFileName);
        // 目标文件:缓存目录下的同名文件
        File targetFile = new File(getCacheDir(), assetFileName);
        outputStream = new FileOutputStream(targetFile);
        
        // 复制流
        byte[] buffer = new byte[1024];
        int length;
        while ((length = inputStream.read(buffer)) > 0) {
            outputStream.write(buffer, 0, length);
        }
    } catch (IOException e) {
        e.printStackTrace();
        Toast.makeText(this, "文件复制失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
    } finally {
        // 关闭流
        try {
            if (inputStream != null) inputStream.close();
            if (outputStream != null) outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

第二步:用FileProvider打开复制后的文件

API24及以上(包括27)禁止跨应用传递file://类型的Uri,必须用FileProvider生成安全的Content Uri,否则会抛出FileUriExposedException。

先配置FileProvider:

  1. 在AndroidManifest.xml的<application>标签里添加Provider配置:
<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

(如果用的是support库,把androidx.core.content.FileProvider改成android.support.v4.content.FileProvider)

  1. 在res/xml目录下创建file_paths.xml文件(如果没有xml目录就新建一个):
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 允许访问应用的缓存目录 -->
    <cache-path name="cache_pdfs" path="." />
</paths>

然后编写打开PDF的代码:

private void openPDFFiles(String assetFileName) {
    // 先把Assets文件复制到缓存
    copyAssetToCache(assetFileName);
    
    // 获取缓存里的目标文件
    File pdfFile = new File(getCacheDir(), assetFileName);
    if (!pdfFile.exists()) {
        Toast.makeText(this, "文件未找到", Toast.LENGTH_SHORT).show();
        return;
    }
    
    // 用FileProvider生成Content Uri
    Uri pdfUri = FileProvider.getUriForFile(
            this,
            getPackageName() + ".fileprovider", // 和Manifest里的authorities一致
            pdfFile
    );
    
    // 构建打开PDF的Intent
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(pdfUri, "application/pdf");
    // 必须添加这个权限标记,允许第三方应用读取这个Uri
    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    
    // 检查设备上有没有能打开PDF的应用
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    } else {
        Toast.makeText(this, "请先安装PDF阅读器应用", Toast.LENGTH_SHORT).show();
    }
}

4. 常见坑点提醒

  • 不要直接用Uri.parse("file:///android_asset/" + assetFileName)来打开,第三方PDF阅读器没有权限访问APK内部的Assets文件
  • 确保AndroidManifest.xml里的authorities和代码里的完全一致,比如你的包名是com.example.myapp,那authorities就是com.example.myapp.fileprovider
  • 如果要保存到外部存储,记得申请READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限(API29及以上不需要,但API27需要)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:07:24