当我尝试打开文件时,应用程序崩溃。它在 Android Nougat 以下运行,但是在 Android Nougat 上崩溃。仅当我尝试从 SD 卡而不是系统分区打开文件时,它才会崩溃。一些权限问题?
样例代码:
File file = new File("/storage/emulated/0/test.txt");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "text/*");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent); // Crashes on this line
日志:
android.os.FileUriExposedException:通过 Intent.getData()在应用之外公开的 file:///storage/emulated/0/test.txt
编辑:
定位 Android 牛轧糖时,不再允许file://
我们应该改用content://
URI。但是,我的应用程序需要打开根目录中的文件。有任何想法吗?
如果您的targetSdkVersion >= 24
,那么我们必须使用FileProvider
类来授予对特定文件或文件夹的访问权限,以使其他应用程序可以访问它们。我们创造我们自己的类继承FileProvider
,以确保我们的 FileProvider 并不冲突与进口的依赖声明 FileProviders 描述这里。
将file://
URI content://
URI 的步骤:
<application>
标记下的AndroidManifest.xml
添加 FileProvider <provider>
> 标记。 android:authorities
属性指定唯一的权限以避免冲突,导入的依赖项可能指定${applicationId}.provider
和其他常用权限。<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
...
<application
...
<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/provider_paths" />
</provider>
</application>
</manifest>
res/xml
文件夹中创建一个provider_paths.xml
如果尚不存在,可能需要创建一个文件夹。文件的内容如下所示。它描述了我们想共享对外部存储在根文件夹(path=".")
,其名称为external_files 。<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="external_files" path="."/>
</paths>
最后一步是更改下面的代码行
Uri photoURI = Uri.fromFile(createImageFile());
至
Uri photoURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", createImageFile());
编辑:如果您使用意图使系统打开文件,则可能需要添加以下代码行:
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
请参考此处已说明的完整代码和解决方案。
FileProvider
的解决方案外,还有另一种方法来解决此问题。简单的说
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
在Application.onCreate()
。这样,VM 会忽略文件URI
公开。
方法
builder.detectFileUriExposure()
启用文件暴露检查,如果我们未设置 VmPolicy,这也是默认行为。
我遇到一个问题,如果我使用content://
URI
发送某些内容,则某些应用程序将无法理解。并且target SDK
版本。在这种情况下,我的解决方案很有用。
更新:
如评论中所述,StrictMode 是诊断工具,不应用于此问题。当我一年前发布此答案时,许多应用程序只能收到 File uris。当我尝试向他们发送 FileProvider uri 时,它们只是崩溃了。现在,这在大多数应用程序中已得到修复,因此我们应该使用 FileProvider 解决方案。
如果targetSdkVersion
高于24 ,则使用FileProvider授予访问权限。
创建一个 xml 文件(路径:res \ xml) provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
在AndroidManifest.xml 中添加提供程序
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
如果您使用的是androidx ,则 FileProvider 路径应为:
android:name="androidx.core.content.FileProvider"
并更换
Uri uri = Uri.fromFile(fileImagePath);
至
Uri uri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider",fileImagePath);
编辑:当您将 URI 与Intent
使用时,请确保添加以下行:
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
而且你走的很好。希望能帮助到你。