在Oreo(仅限)中引起android.os.FileUriExposedException的原因是什么?

4

从Google Play控制台可以看到,这个异常只在安卓8.0及以上的设备上发生。

android.os.FileUriExposedException: 
at android.os.StrictMode.onFileUriExposed (StrictMode.java:1975)
at android.net.Uri.checkFileUriExposed (Uri.java:2355)
at android.content.Intent.prepareToLeaveProcess (Intent.java:9975)
at android.content.Intent.prepareToLeaveProcess (Intent.java:9950)
at android.content.Intent.prepareToLeaveProcess (Intent.java:9929)
at android.app.Instrumentation.execStartActivity (Instrumentation.java:1622)
at android.app.Activity.startActivityForResult (Activity.java:4748)
at android.support.v4.app.BaseFragmentActivityJB.startActivityForResult (BaseFragmentActivityJB.java:10)
at android.support.v4.app.FragmentActivity.startActivityForResult (FragmentActivity.java)
at android.support.v4.app.ActivityCompatJB.finishAffinity (ActivityCompatJB.java)
or                                         .startActivityForResult (ActivityCompatJB.java)
at android.support.v4.app.ActivityCompat.a (ActivityCompat.java:6)
at android.support.v4.app.FragmentActivity.a (FragmentActivity.java:8)
at android.support.v4.app.FragmentActivity$HostCallbacks.a (FragmentActivity.java:2)
at android.support.v4.app.Fragment.a (Fragment.java:38)
at android.support.v4.app.Fragment.a (Fragment.java:1)
at de.test.testapp.FileManagerFragment.onClick (FileManagerFragment.java:113)
at android.view.View.performClick (View.java:6291)
at android.view.View$PerformClick.run (View.java:24931)
at android.os.Handler.handleCallback (Handler.java:808)
at android.os.Handler.dispatchMessage (Handler.java:101)
at android.os.Looper.loop (Looper.java:166)
at android.app.ActivityThread.main (ActivityThread.java:7390)
at java.lang.reflect.Method.invoke (Method.java)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run (Zygote.java:245)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:926)

代码如下:
Intent intent = new Intent(Intent.ACTION_VIEW);
ArrayList<Uri> uri = new ArrayList<Uri>();
for (int i=0; i<checkedItems.size(); i++) {
    if (checkedItems.valueAt(i)) {
        intent.setDataAndType(Uri.fromFile(new File(projectDir, filename), "text/plain");
    }
}
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uri);
startActivity(Intent.createChooser(intent, "Open RINEX File"));

清单文件:

<?xml version="1.0" encoding="utf-8"?>

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<!-- To access Google+ APIs: -->
<uses-permission android:name="android.permission.INTERNET" />

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity
        android:name=".MainActivity"
        android:screenOrientation="portrait">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="de.test.testapp"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_providers_paths" />
    </provider>
</application>

file_providers_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>

为什么这在Android 7上可以正常工作,但在Android 8上不行了?有哪些行为变化发生了?我在一个片段中调用它。

很不幸,我这里没有安卓8设备可以测试。你可以在模拟器上进行测试。另外,你确定你看到的错误是来自于这个代码版本吗? - CommonsWare
@Jaimin Thakkar:我将该文件内容添加到了我的帖子中。 - Armai
@greenapps:非常抱歉。请假设文件路径正确,因为它在应用程序的许多其他方面中使用,且没有发现任何问题。 - Armai
不,该文件路径应该与提供者的路径相对应。因此,请让我们检查一下。 - greenapps
我看不到完整的文件路径。而且你已经检查过一个非常糟糕的答案了。对我来说,它看起来还是同样的代码。 - greenapps
显示剩余3条评论
3个回答

15

尝试将以下内容添加到onCreate内。

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
builder.detectFileUriExposure();

1
谢谢,你做到了 :) - Armai
1
最合适的答案。 - Danger
1
非常感谢,它正在运行 :) - Josss
1
@CommonsWare提到这种方法在Android 10及更高版本中会失败,我在另一个问题中也看到了。所以这似乎不是完美的方式。 - Amir Dora.

5
如果您的应用程序使用Uri与其他应用程序共享文件,则可能会在API 24+上遇到此错误。
当您尝试在Intent广播中共享一个file:// Uri以共享数据与其他应用程序时,就会出现此错误。在这种情况下,不建议使用file:// Uri,因为它对目标应用程序进行了一些假设。首先,我们假设目标应用程序具有READ_EXTERNAL_PERMISSION权限,但实际上可能并非如此。如果目标应用程序没有READ_EXTERNAL_PERMISSION权限,则可能会导致意外行为,最坏的情况下会导致崩溃。
从Android N开始,为解决此问题,您需要使用FileProvider API。
步骤1:清单条目
<manifest ...>
    <application ...>
        <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>
    </application>
</manifest>

第二步:创建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>

第三步:代码更改
File file = ...;
Intent install = new Intent(Intent.ACTION_VIEW);
install.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
// Old Approach
install.setDataAndType(Uri.fromFile(file), mimeType);
// End Old approach
// New Approach
Uri apkURI = FileProvider.getUriForFile(
                         context, 
                         context.getApplicationContext()
                         .getPackageName() + ".provider", file);
install.setDataAndType(apkURI, mimeType);
install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// End New Approach
context.startActivity(install);

0

这是由于Android 8及以上版本,无论您以编程方式拍摄什么图像,都不会存储到图库中,因此您无法访问图库文件或其URI。如果实现任何图像捕获功能,则需要将捕获的图像实现并存储到资源文件夹中,然后从那里获取URI。这就是为什么我们要这样做:

android:resource="@xml/file_providers_paths"

实际上,从用户的安全角度来看,这是很好的。


我这里根本没有处理图像文件,而且那一行已经包含在清单文件中了,你有注意到吗? - Armai
我并不仅将其归纳为图像文件,无论是创建文本文件,我都试图回答您的问题:为什么这在Android 7上可以工作,但在Android 8上不再工作?发生了什么行为变化?此外,我也遇到过这种情况,并且已经处理过它,而不必编写上述代码。 - Elathan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接