Introduction
Starting with Android 7.0 (Nougat), a new security restriction was introduced to enhance app safety by preventing file://
URIs from being exposed beyond an app through implicit intents. This change, while beneficial for security, has posed challenges for developers when handling file sharing and opening operations. If your application crashes due to a FileUriExposedException
, this tutorial will guide you on how to resolve the issue using the recommended approach: the FileProvider
class.
The Problem
When attempting to open files from external storage (e.g., SD card) in Android Nougat and above, directly passing a file URI can cause your app to crash. This is due to Android’s restriction on exposing file://
URIs:
android.os.FileUriExposedException: file:///storage/emulated/0/test.txt exposed beyond app through Intent.getData()
This exception indicates that the application attempts to share a local file using an unsupported URI scheme, leading to runtime failures.
Solution: Using FileProvider
To circumvent this limitation, Android provides FileProvider
, which allows secure sharing of files via content://
URIs. Here’s how you can implement it:
Step 1: Update Your AndroidManifest.xml
Firstly, add a <provider>
tag to your AndroidManifest.xml
. This provider uses androidx.core.content.FileProvider
.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="your.package.name">
<application ... >
<!-- Other components like activities -->
<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/provider_paths" />
</provider>
</application>
</manifest>
Step 2: Define File Provider Paths
Create an XML file named provider_paths.xml
in the res/xml
directory. This file specifies which files or directories should be shared:
<?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: Modify Your Code to Use FileProvider
Replace instances where you use Uri.fromFile(file)
with FileProvider.getUriForFile()
:
import android.content.Intent;
import android.net.Uri;
import androidx.core.content.FileProvider;
// Assuming 'file' is your File object and 'context' is a valid Context.
Uri fileUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", file);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(fileUri, "text/*");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
Step 4: Grant URI Permissions
When creating an Intent
to open the file with another application, ensure you add the flag to grant temporary read permission:
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Additional Tips and Best Practices
-
Unique Authority: Ensure the
android:authorities
attribute is unique across your app. This prevents conflicts if you have multiple providers. -
Security Considerations: Always validate paths to ensure that no sensitive files are inadvertently exposed.
-
Testing: Thoroughly test on different Android versions and scenarios, especially when sharing files with external apps.
By following these steps, you can securely share files from your app without encountering FileUriExposedException
. This approach not only complies with Android’s security standards but also enhances the safety of file operations across applications.