Unity Android API23及以上版本如何调用PDF阅读器打开PDF文件
我之前帮不少开发者处理过这个问题,核心原因是Android 6.0(API23)之后的权限机制和文件访问限制发生了关键变化,尤其是API24开始的FileUriExposedException限制,直接导致旧的file:// Uri方式无法正常工作。下面给你一步步的解决方案:
为什么原来的方法失效?
从Android 6.0(API23)开始,系统引入了运行时权限机制,读取外部存储需要动态申请权限;而从Android 7.0(API24)开始,直接通过file:// Uri分享应用私有文件给其他应用会触发安全异常,系统禁止这种不安全的跨应用文件访问方式,所以你的Application.OpenUrl("file://" + path)就失效了。
解决方案:用FileProvider实现安全的文件共享
下面是完整的实现步骤:
1. 在AndroidManifest.xml中注册FileProvider
在<application>标签内添加FileProvider的配置,它是Android提供的安全共享文件的组件:
<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;authorities建议用你的应用ID加上.fileprovider,避免和其他应用冲突。
2. 创建文件路径配置文件
在res/xml目录下新建file_paths.xml文件(如果xml目录不存在就手动创建),用来定义允许共享的文件目录:
<paths xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 共享应用内部存储的文件 --> <files-path name="internal_files" path="." /> <!-- 共享应用专属外部存储的文件 --> <external-files-path name="external_files" path="." /> <!-- 共享外部公共目录的文件(比如SD卡根目录) --> <external-path name="external_public" path="." /> </paths>
按需选择对应的路径标签:
files-path对应应用内部存储的/data/data/[你的应用ID]/files/目录external-files-path对应外部存储的Android/data/[你的应用ID]/files/目录external-path对应外部存储的根目录
3. 动态申请存储权限(如果文件在外部存储)
如果你的PDF文件存放在外部存储(比如SD卡),API23及以上需要动态申请READ_EXTERNAL_STORAGE权限:
// 检查权限是否已授予 if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { // 申请权限 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1001); } else { // 权限已授予,直接打开PDF openPdfFile(pdfFile); } // 处理权限申请结果 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == 1001) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { openPdfFile(pdfFile); } else { Toast.makeText(this, "需要存储权限才能打开PDF文件", Toast.LENGTH_SHORT).show(); } } }
4. 生成安全的Content Uri并打开PDF
编写openPdfFile方法,根据系统版本选择Uri生成方式,然后启动Intent打开PDF:
private void openPdfFile(File pdfFile) { Uri pdfUri; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // API24及以上,用FileProvider生成安全的Content Uri pdfUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", pdfFile); } else { // API23及以下,直接用file:// Uri(注意权限问题) pdfUri = Uri.fromFile(pdfFile); } // 构建打开PDF的Intent Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(pdfUri, "application/pdf"); // 授予临时读取权限给目标应用 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // 确保有应用能处理这个Intent if (intent.resolveActivity(getPackageManager()) != null) { startActivity(intent); } else { Toast.makeText(this, "未找到可打开PDF的应用", Toast.LENGTH_SHORT).show(); } }
额外注意事项
- 确保你的PDF文件路径正确,文件存在且具备可读权限
- 如果是Unity等跨平台框架调用,需要把上述Java代码封装成Android插件,或者用框架提供的Android交互API调用对应方法
- 测试时重点验证Android 7.0及以上设备,避免触发
FileUriExposedException
内容的提问来源于stack exchange,提问作者Afzal Halai

