android.os.FileUriExposedException being caused in

2019-05-23 03:27发布

From the Google Play Console, I can see that this exception is only happening on devices with Android 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)

the code:

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"));

Manifest:

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

Why does this work on Android 7 but not on Android 8 anymore? What behaviour changes have happened? I am calling this within a fragment.

3条回答
祖国的老花朵
2楼-- · 2019-05-23 03:36

If you have an app that shares files with other apps using a Uri, you may have encountered this error on API 24+.

This error occurs when you try to share a file:// Uri in an Intent broadcast to share data with other apps. Using file:// Uri’s are discouraged in this scenario because it makes some assumptions about the destination app. For one thing, we assume that the destination app has READ_EXTERNAL_PERMISSION which may not be the case. If the destination app does not have READ_EXTERNAL_PERMISSION, this may result in unexpected behaviour at best or at worst, result in a crash.

As of Android N, in order to work around this issue, you need to use the FileProvider API.

Step 1: Manifest Entry

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

Step 2: Create XML file 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>

Step 3: Code changes

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);
查看更多
Root(大扎)
3楼-- · 2019-05-23 03:52

This is cause from android 8 on wards whatever image you take programmatically won't be stored to the gallery, hence you cannot access the gallery files or its URI's, If implementing any image capturing functionality you need to implement and store the captured image to your resources folder and from there you will get the URI. Thats why we do

android:resource="@xml/file_providers_paths"

Actually its good from the security point of view for the user.

查看更多
你好瞎i
4楼-- · 2019-05-23 03:53

Try adding this inside onCreate.

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
builder.detectFileUriExposure();
查看更多
登录 后发表回答