In development, it is often necessary to pass a file to another application, such as uploading an image to another application or copying and pasting files between different storage paths. Sharing files requires understanding that the receiving application is sending a request to the providing application.
Starting from Android 7.0, Android enforces the StrictMode policy, which prohibits exposing file://URLs outside of the application. If an application above Android 7.0 does not use FileProvider, it will throw a FileUriExposedException. To share files between applications after Android 7.0, you need to use content://URL to grant temporary access permissions to the URL. This means using FileProvider to grant temporary access permissions. URLs with temporary access permissions are secure, as they automatically expire. The getUriForFile() provided by FileProvider is used to generate the content of the file.
In all cases, the only secure way for your application to provide a file to another application is to send the content URI of the file to the receiving application and grant temporary access permissions to that URI. Content URIs with temporary access permissions are secure because they only apply to the application receiving the URI and they automatically expire. The Android FileProvider component provides the getUriForFile() method to generate content URIs for files.
This also mentions a common exception that occurs in Android 7.0 and higher versions: FileUriExposedException. This exception can be resolved by using FileProvider, which is a result of the continuous improvement of security in the Android system.
- Specify FileProvider
- Specify file sharing path
Specify FileProvider#
Specify the Provider in the AndroidManifest file, as shown below:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">
<application
...>
<!--android:authorities="${applicationId}.yourname"-->
<provider
android:name="android.support.v4.content.FileProvider"
<!--The authorities attribute specifies the URI permission to be used for the content URI generated by FileProvider, generally in the format of applicationId.yourname-->
android:authorities="com.example.myapp.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
...
</application>
</manifest>
Specify file sharing path#
In the above code, the file directory to be shared is specified in the meta-data directory, and the file directory is defined in filepathd.xml. The paths that can be defined in the corresponding xml are as follows:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<paths>
<!--Represents the root directory of the device (new File("/"))-->
<root-path name="root" path="" />
<!--Represents context.getFileDir()-->
<files-path name="files" path="" />
<!--Represents context.getCacheDir()-->
<cache-path name="cache" path="" />
<!--Represents Environment.getExternalStorageDirectory()-->
<external-path name="external" path="" />
<!--Represents context.getExternalFilesDirs()-->
<external-files-path name="name" path="path" />
<!--Represents getExternalCacheDirs()-->
<external-cache-path name="name" path="path" />
</paths>
</resources>
In the xml, a certain path is represented by two attributes. The path attribute represents the subdirectory of the current specified directory. If not specified, it represents the root directory and its subdirectories of the current specified directory. The name attribute represents the URL that will be added after name as the access path for the file. For example:
//Represents that the file to be shared will be searched in the images subdirectory under context.getFileDir() directory
<paths>
<files-path path="images/" name="myImage" />
</paths>
//Represents the final generated shared file URL
content://com.example.myapp.fileprovider/myImage/image.jpg
Get Uri#
Finally, after the configuration is complete, when obtaining the URL for files, it should be obtained as follows:
public Uri getUri(File file) {
Uri uri = null;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
uri = FileProvider.getUriForFile(mContext, mContext.getPackageName() + ".youName", file);
} else {
uri = Uri.fromFile(file);
}
return uri;
}
This way, you can easily share files above Android 7.0.