This article tags: Android7.0 Fileprovider
Because Android 7.0 or later systems in the domestic mobile phone market is not very high, many Android developers do not do 7.0 adaptation work, and testers can easily ignore this aspect of compatibility issues. This results in the app crash flashback for mobile phone users 7.0 and older when they use the App section feature. Most of these are caused by a URI that is used in the project to the file:// type. In this article we will come to a probe.
Android 7.0 Permissions Change
In order to improve the security of the private directory, to prevent the leakage of application information, starting from Android 7.0, the application of private directory access is limited. Specifically, developers can no longer simply access the private directory files of other applications via the file:// URI or allow other apps to access their own private directory files.
Note: If you're not sure about the app's private directory, you can read my article: Learn about the file storage directory for Android apps and get the right posture for persistent data.
Also, starting from 7.0, the Strictmode policy in the Android SDK prohibits developers from exposing file:// URIs outside the app. Specifically, the program fails when we leave our app with Intent that contains the file:// URI in the app. Original address: http://whosmall.com/?post=451
In development, if we ignore these two rules when using the file:// URI, the fileuriexposedexception exception occurs when the user uses the relevant functionality in devices on the 7.0 and later systems. Causes the app to crash and flash back up. The alternative solution to these two processes is to use FileProvider
.
Fileprovider
As one of the four components ContentProvider
, it has been playing the role of sharing resources among applications. What we're going to use here FileProvider
is ContentProvider
a special subclass that helps us turn the restricted file:// uri into a content:// URI that can be licensed for sharing.
The first step is to register a Fileprovider
As one of the four components of the system ContentProvider, its subclasses fileprovider, also need to use elements in the Manifest file to add registration information, and set the relevant property values as required.
1234567891011 |
<application> ... <provider android:name= "Android.support.v4.content.FileProvider" android:authorities= "${ Applicationid}.yourname " android:exported=" false " android:granturipermissions=" true "> ... </provider> ...</application> |
Where the android:authorities
property value is a Uri string consisting of the ApplicationID value and the custom name in the Build.gradle file (this is customary). Other property values can be used as a fixed value.
Second step, add the shared directory
Create a new XML file in the Res/xml directory to store the directory files that your app needs to share. The content of this XML file is similar to this:
12345 |
<?xml version= "1.0" encoding= "Utf-8"? ><paths xmlns:android= "Http://schemas.android.com/apk/res/android" > <files-path name= "my_images" path= "images/"/> ...</paths> |
The element must contain one or more child elements. These child elements are used to specify the directory path of the shared file, which must be one of these elements:
<files-path>
: The files/directory under the private directory of the internal storage space, equivalent to the Context.getFilesDir()
directory path obtained;
<cache-path>
: The cache/directory under the private directory of the internal storage space, equivalent to the Context.getCacheDir()
directory path obtained;
<external-path>
: The root of the external storage space, equivalent to the Environment.getExternalStorageDirectory()
directory path obtained;
<external-files-path>
: The files/directory under the Private directory of the external storage space, equivalent to the Context.getExternalFilesDir(null)
directory path obtained;
<external-cache-path>
: The cache/directory under the Private directory of the external storage space, equivalent to Context.getexternalcachedir ();
As you can see, these five seed elements basically cover all directory paths inside and outside the storage space, including the app's private directory. At the same time, each child element has a name and path two properties.
Where the Path property is used to specify the name of the subdirectory that the current child element represents in the directory that needs to be shared. Note: The Path property value cannot use a specific stand-alone file name, only the directory name.
The Name property is used to give an alias to the subdirectory name specified by the Path property. This alias is used instead of the real directory name when the content:// URI is subsequently generated. The purpose of this is obviously to improve security.
If the files we need to share are in different subdirectories under the same level directory, you will need to add multiple child elements to specify the file directories to share, or share their common parent directory.
After adding the shared directory, <provider>
use elements in the element <meta-data>
to link the path file in Res/xml with the registered Fileprovider:
123456789 |
<provider android:name= "Android.support.v4.content.FileProvider" android:authorities= "${ Applicationid}.yourname " android:exported=" false " android:granturipermissions=" true "> < Meta-data android:name= "Android.support.FILE_PROVIDER_PATHS" android:resource= "@xml/yourfilename"/> </provider> |
Step three, generate the Content URI
Before Android 7.0 appeared, we typically used Uri.fromFile()
methods to generate a File URI. Here, we need to FileProvider
generate the Content URI using the public static method provided by the class getUriForFile
. Like what:
12 |
Uri Contenturi = Fileprovider.geturiforfile (this, buildconfig.application_id + ". MyProvider", myFile); |
You need to pass three parameters. The second parameter is the authorities property value set when registering Fileprovider in the Manifest file, the third parameter is the file to be shared, and the file must be located in the second step of the subdirectory we added in the path file.
As an example:
1234567 |
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 + ". MyProvider", outputFile); |
The resulting Content URI is this:
1 |
Content://com.yifeng.samples.myprovider/my_images/1493715330339.jpg |
Where the host part of the URI is the <provider>
element's authorities property value (ApplicationID + customname), and the path fragment my_images to the subdirectory alias specified in the Res/xml file (the real directory name is: Images).
Fourth step, grant the Content URI access permission
After the Content URI object is generated, it needs to be granted access rights. There are two ways of authorizing:
The first is to use the method provided by the Context grantUriPermission(package, Uri, mode_flags)
to grant access to the URI object to other applications. The three parameters represent the other app package names that grant access to the URI object, the URI object that is authorized to access, and the authorization type. Where the authorization type is the read-write type constant provided by the Intent class:
Or they are authorized at the same time. This form of authorization means that the permission expires until the device restarts or revokeUriPermission()
when the method revocation authorization is invoked manually.
The second way, with the use of Intent. setData()
adds a Content URI to the intent object by method. Then use setFlags()
or addFlags()
method to set the read and write permissions, optional constant value ibid. This form of authorization means that the permissions are valid for the stack destroyed by other applications, and once a component is authorized, the other components of the app have the same access rights.
Fifth step, provide the Content URI to other applications
Once you have a content URI that grants permissions, you startActivity()
can setResult()
start other apps and pass the content URI data that you've authorized, either by means of a method. Of course, there are other ways to provide services.
If you need to pass multiple URI objects at once, you can use the methods provided by the intent object, setClipData()
and setFlags()
the permissions set by the method apply to all Content URIs.
Common usage scenarios
The previous sections are theoretical and are described in the official Fileprovider section of the developer. Next we look at the actual development of an application in the process, will often meet the fileprovider of the use of the scene.
Automatic installation files
When the version update is complete, opening a new version of the APK file for automatic installation should be the most common usage scenario and one of the essential features of each application. A common action is that the notification bar displays the download of the new version complete, the user taps or listens to the download process to automatically open a new version of the APK file. Before adapting to the Android 7.0 version, our code might be like this:
123456 |
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); |
You must now use the Content URI instead of the File URI in order to fit a system of version 7.0 and above.
Create a new File_provider_paths.xml file (the file name is freely defined) under the Res/xml directory and add the subdirectory path information:
123456 |
<?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 the path path file above:
1234567891011 |
<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 a Content URI object from the File object and grant access to:
123456789 |
File Apkfile = new file (Getexternalfilesdir (environment.directory_downloads), "app_sample.apk"); Uri Apkuri = Fileprovider.geturiforfile (this, buildconfig.application_id+ ". MyProvider", apkfile); Intent Installintent = new Intent (Intent.action_view); Installintent.addflags (Intent.flag_activity_new_task); Installintent.addflags (intent.flag_grant_read_uri_permission); Installintent.setdataandtype (ApkUri, "application /vnd.android.package-archive "); StartActivity (installintent); |
This completes the 7.0 adaptation of the APK file that is called System functionality in the app.
Call system to take pictures
You also need to pass a Uri object when you call the System capture feature to save the image to the specified directory, which also needs to be adapted to version 7.0. The other steps are no longer mentioned, the core Java code is as follows (the path is different, note add Res/xml in the path file subdirectory):
1234567891011 |
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 + ". MyProvider", outputFile); Intent Intent = new Intent (mediastore.action_image_capture); Intent.putextra (Mediastore.extra_output, Contenturi); Startactivityforresult (Intent, request_take_picture); |
Call system cropping
The process of calling system clipping involves two Uri objects: Inputuri and Outputuri, which are more complex. Typically, the source that calls the system crop takes a picture of the calling system or selects a system album. The former returns a File URI object, which returns a Content URI object. As a clipping source, all we have to do is to deal with it further. However, it is not difficult to use the method as above getUriForFile()
, it is not hard to understand, because if you choose the picture of the System album, it does not belong to our own application. The correct way to handle this is:
123456789101112131415 |
Private Uri Getimagecontenturi (String path) { cursor cursor = getcontentresolver (). Query ( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, New string[]{mediastore.images.media._id}, MediaStore.Images.Media.DATA + "=?", new String[]{path}, NULL); if (cursor! = NULL && Cursor.movetofirst ()) { int id = cursor.getint (cursor.getcolumnindex ( mediastore.images.media._id)); Uri BaseUri = Uri.parse ("Content://media/external/images/media"); Return Uri.withappendedpath (BaseUri, "" +id); } else { Contentvalues contentvalues = new Contentvalues (1); Contentvalues.put (MediaStore.Images.Media.DATA, path); Return Getcontentresolver (). Insert (MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentvalues);} } |
After getting the correct Content URI, pass it to the Intent object as Inputuri:
12345678 |
Intent Intent = new Intent ("Com.android.camera.action.CROP"); Intent.addflags (Intent.flag_grant_write_uri_ PERMISSION | intent.flag_grant_read_uri_permission); Intent.setdataandtype (Inputuri, "image/*"); Intent.putextra ("Crop", "true" ); Intent.putextra ("Aspectx", 1); Intent.putextra ("Aspecty", 1); Intent.putextra (Mediastore.extra_output, Uri.fromfile (OutputFile)); Startactivityforresult (Intent, Request_pick); |
Note: The Outputuri here does not change, still using the Uri.fromFile()
method gets the File URI type! This is a strange point, but it has to be done. In fact, it is problematic to call the system clipping function in this way! Crop Intent in the official document is no trace, in itself is a non-recommended usage! Instead, we can use some of the open source libraries on GitHub to implement in-app image cropping, such as Ucrop, Cropper, and more.
Historical version Issues
To say so much, there is a concern for everyone is: which has been on the line of the old version of the application did not do 7.0 adaptation work to do? Google has helped us find a solution to this problem in advance.
Do you remember 6.0 runtime permissions issues? If you do not want to handle runtime permissions, simply set the value of Targetsdkversion to 23 in the Build.gradle file.
Similarly, as long as the targetsdkversion value is less than the 24,file URI, it can still appear on devices of version 7.0 and above. However, it is important to note that, as mentioned earlier, the call system clipping feature is special, some problems may occur.
While Google offers this setup targetsdkversion in a way that is compatible with older versions each time a new version of Android is released, it is only a temporary solution, and it is not recommended to use this technique to circumvent the adaptation of a new release. You know, the new API changes must be in the past to solve the system problems, is a progressive performance. Following the specification is the maxim that every developer should bear in mind when developing.
This article was created by Maple and started in Maple's personal blog
written at the end: for Freedom look outside the world, and it this line, not to go to Google data, finally, Amway some speed agent.
Accelerator recommendations |
Free Solutions |
Payment Plan |
Official website |
A Red apricot accelerator |
Free program is not available, stable high-speed |
Enter 80 percent coupon code WH80, annual pay only 80 yuan/year |
Website Direct |
Azumino Accelerator |
Best use of foreign trade VPN |
Minimum ¥30/Month |
Website Direct |
Loco Accelerator |
Free 2 hours per day |
Minimum ¥15/Month |
Website Direct |
This article tags: Android7.0 Fileprovider
Turn from SUN's BLOG-focus on Internet knowledge, share the spirit of the Internet!
Original Address : "Let's explore the Fileprovider part of Android 7.0 adaptation "
Related reading : Chrome extension Stylish: "Skin-changing" with one click to not like a website
Related reading : "Integrating QQ music, NetEase cloud music and shrimp music resources" with chrome extension listen 1 "
Related reading : " 8" new tab "chrome Extensions: teach you to play the New tab page with a sneak"
Related reading : "7 Practical Chrome Extensions recommended: help you improve your chrome experience"
Related reading : "No extension is not Chrome: 15 Premium Chrome Extensions recommended for everyone"
Related reading : the best experience for Web browsing with 12 no less chrome extensions
Related reading : "5 Chrome extensions that bring happiness"
Related reading : "Use case for Palette class in Android: Color adaptive Toolbar"
Related reading: What git can do, how it differs from SVN in depth
Related reading: "Share some of the most useful, user-friendly, and feature-rich Google Chrome Extensions for developers"
Related reading: a lot of brief encounter tools or websites that share some of the actual Android development process
related reading:"I am a G powder, has been concerned about Google, recently Google has some little gestures, probably a lot of people do not understand"
related reading:"machine learning leads to technological innovation in the field of cognition, so how can the SaaS industry be changed by machine learning?" 》
related reading:"VPS Tutorial Series: DNSMASQ + DNSCrypt + SNI Proxy smooth access to Google configuration tutorial"
related reading: useful to programmers: 2017 latest in Google's Hosts file download and summary of the various hosts encountered the problem of the solution and configuration of the detailed
related Blog:sun ' S BLOG -Focus on Internet knowledge and share the spirit of Internet! Go and see:www.whosmall.com
Original address: http://whosmall.com/?post=451
Fileprovider Part Summary of Android 7.0 adaptation