Detailed Explanation of FileProvider Attribute Configuration Based on Android and FileProvider Multi node Problem

  • 2021-11-29 08:34:11
  • OfStack

As we all know, in android7.0, the restriction on private storage was modified, which led to the inability to obtain uri through Uri. fromFile when obtaining resources. We need to adapt to the 7.0 + model and need to write as follows:

1: Code adaptation


 if (Build.VERSION.SDK_INT > 23) {//
        intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        Uri contentUri = FileProvider.getUriForFile(context, SysInfo.packageName + ".fileProvider", outputFile);
        intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
      } else {
        intent.setDataAndType(Uri.fromFile(outputFile), "application/vnd.android.package-archive");
      }

2: Create provider_paths. xml


<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
  <!-- /storage/emulated/0/Download/${applicationId}/.beta/apk-->
  <external-path name="beta_external_path" path="Download/"/>
  <!--/storage/emulated/0/Android/data/${applicationId}/files/apk/-->
  <external-path name="beta_external_files_path" path="Android/data/"/>
 
</paths>

Detailed explanation of provider_path attribute

name and path

name: uri path fragment. For security purposes, this value hides the subdirectory names you share. The subdirectory name of this value is included in the path property.

path: Subdirectories you share. Although the name attribute is an URI path fragment, path is a real subdirectory name. Note that path is a subdirectory, not a single file or multiple files.

1.files-path

Represents the same file path as Context. getFileDir ()

2.cache-path

< cache-path name="name" path="path" / >

Represents the same file path as getCacheDir ()

3.external-path

< external-path name="name" path="path" / >

Represents the same file path as Environment. getExternalStorageDirectory ()

4.external-files-path

< external-files-path name="name" path="path" / >

Represents the same file path as Context # getExternalFilesDir (String) and Context. getExternalFilesDir (null)

5.external-cache-path

< external-cache-path name="name" path="path" / >

Represents the same file path as Context. getExternalCacheDir ()

6: Configure AndroidManifest. xml

android: authorities is used in FileProvider


<provider 
  android:name="android.support.v4.content.FileProvider" 
  android:authorities="com.mydomain.fileprovider" 
  android:exported="false" 
  android:grantUriPermissions="true"> 
  <meta-data 
    android:name="android.support.FILE_PROVIDER_PATHS" 
    android:resource="@xml/file_paths" /> 
</provider> 

7: Use FileProvider


***  Return URI : content://com.mydomain.fileprovider/my_images/default_image.jpg.

File imagePath = new File(Context.getFilesDir(), "images"); 
File newFile = new File(imagePath, "default_image.jpg"); 
Uri contentUri = getUriForFile(getContext(), "com.mydomain.fileprovider", newFile); 

8. Customize FileProvider


class MyFileProvider extends FileProvider {}

You can configure android: authorities in AndroidMenifest. xml

3: If other third-party sdk is useful for taking pictures in our project, he also added this node to adapt to android7.0. At this time, some people may not know how to start. In fact, it is very simple. We only need to rewrite a class to inherit from FileProvider, and then add a node according to the above method:


<provider
  android:name="com.blueZhang.MyFileProvider"
  android:authorities="${applicationId}.provider"
  android:grantUriPermissions="true"
  android:exported="false">
  <meta-data
    android:name="android.support.FILE_PROVIDER_PATHS"
    android:resource="@xml/cust_file_paths" />
</provider>

If you don't want to customize FileProvider, there is another way, that is, configure copy to provider_paths. xml in the third party sdk.

As shown below:


<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
  <!-- /storage/emulated/0/Download/${applicationId}/.beta/apk-->
  <external-path name="beta_external_path" path="Download/"/>
  <!--/storage/emulated/0/Android/data/${applicationId}/files/apk/-->
  <external-path name="beta_external_files_path" path="Android/data/"/>
 
  <external-path name="external_storage_root" path="."/>
  <files-path name="files" path="."/>
 
</paths>

Note: When using provider, configure the path path= "." for all paths

Generate Content URI

Before Android 7.0, we typically used the Uri. fromFile () method to generate an File URI. Here, we need to generate Content URI using the public static method getUriForFile provided by the FileProvider class.

For example:

Uri contentUri = FileProvider.getUriForFile(this,
BuildConfig.APPLICATION_ID + ".fileProvider", myFile);


You need to pass 3 parameters. The second parameter is the authorities attribute value set when registering FileProvider in Manifest file, and the third parameter is the file to be shared, and this file 1 is located in the subdirectory we added in path file in step 2.

For example:


String filePath = Environment.getExternalStorageDirectory() + "/images/"+System.currentTimeMillis()+".jpg";
File outputFile = new File(filePath);
if (!outputFile.getParentFile().exists()) {
  outputFile.getParentFile().mkdir();
}
Uri contentUri = FileProvider.getUriForFile(this,
    BuildConfig.APPLICATION_ID + ".fileProvider", outputFile);

The generated Content URI looks like this:

content://com.yifeng.samples.myprovider/my_images/1493715330339.jpg

Among them, the host part constituting URI is < provider > Element's authorities attribute value (applicationId + customname), the path fragment my_images is the subdirectory alias specified in the res/xml file (real directory name: images).

Step 4, Grant Content URI Access

Once an Content URI object is generated, it needs to be granted access. There are two ways to authorize:

In the first way, grantUriPermission (package, Uri, mode_flags) methods provided by Context are used to authorize access to URI objects to other applications. The three parameters represent the names of other application packages authorized to access the URI object, the Uri object authorized to access, and the authorization type. Where the authorization type is the read-write type constant provided by the Intent class:

FLAG_GRANT_READ_URI_PERMISSION

FLAG_GRANT_WRITE_URI_PERMISSION

Or both are authorized at the same time. This form of authorization is valid until the device is restarted or the authorization is revoked by manually calling the revokeUriPermission () method.

The second mode is used in conjunction with Intent. Add Content URI to the intent object through the setData () method. Then use the setFlags () or addFlags () method to set the read and write permissions, optional constant values as above. In this form of authorization, the validity period of permission expires until the stack where other applications are located is destroyed, and once one component is authorized, other components of the application have the same access permission.

Step 5: Provide Content URI to other applications

Once you have an Content URI that grants permission, you can start other applications and pass the authorized Content URI data through the startActivity () or setResult () methods. Of course, there are other ways to provide services.

If you need to pass multiple URI objects at once, you can use the setClipData () method provided by the intent object, and the permissions set by the setFlags () method apply to all Content URIs.

Common usage scenarios

The contents introduced above are all theoretical parts, which are introduced in the official FileProvider part of developers. Next, let's take a look at the usage scenarios of FileProvider that are often encountered in the actual development of an application.

Automatically install files

Opening the new version apk file to realize automatic installation when the version update is completed should be the most common use scenario, and it is also one of the necessary functions of every application. The common operation is that the notification bar shows that the new version is downloaded, and the user clicks or listens to the download process to automatically open the new version apk file. Before adapting to Android version 7.0, our code might look like this:


File apkFile = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "app_sample.apk");
 
Intent installIntent = new Intent(Intent.ACTION_VIEW);
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
installIntent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
startActivity(installIntent)

Content URI must now be used instead of File URI in order to adapt to versions 7.0 and above.

Create a new file_provider_paths. xml file under the res/xml directory (file name is freely defined) and add subdirectory path information:


<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
 
  <external-files-path name="my_download" path="Download"/>
 
</paths>

Then register the FileProvider object in the Manifest file and link to the path path file above:


<provider
  android:name="android.support.v4.content.FileProvider"
  android:authorities="com.yifeng.samples.myprovider"
  android:exported="false"
  android:grantUriPermissions="true">
 
  <meta-data
    android:name="android.support.FILE_PROVIDER_PATHS"
    android:resource="@xml/file_provider_paths"/>
 
</provider>

Modify the java code to generate an Content URI object from an File object and grant access to:


<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
  <!-- /storage/emulated/0/Download/${applicationId}/.beta/apk-->
  <external-path name="beta_external_path" path="Download/"/>
  <!--/storage/emulated/0/Android/data/${applicationId}/files/apk/-->
  <external-path name="beta_external_files_path" path="Android/data/"/>
 
</paths>
0

All right, if you don't understand, contact me in time


Related articles: