Abstract: Principles and best practices of partitioned storage!

Android application development team

A few years ago, one of the biggest complaints about Android was file storage, which was poorly distributed and managed in a fragmented way. However, as the user base of Android system is very large, the file storage fragmentation of Android system is becoming increasingly acute.

Fortunately, with the recent iteration of Android, file management has been officially put on the agenda. In Android 10 and beyond, a sandbox mechanism called “partitioned storage” has been introduced to limit the ability of applications to access files. Avoid some inferior or harmful applications to create or delete files that are not generated by themselves, maximizing the protection of the Android file management ecology virtuous cycle.

Previously, file access was very simple for developers and did not involve adaptation work. However, partitioned storage beyond Android 10 forces applications to use URIs to access specified files. However, the best implementation is provided on the official website, and I will post it at the end of the article for developers to see for themselves.

In this article, we will focus on how to manipulate files in Native (C/C ++) layers using urIs given by Android. Android officials only briefly mention how to do this, but in fact, in many real-world work scenarios, should be a very common feature. This guide will be attached to the core code implementation, I believe that you will have greater reference value.

Introduction to partition storage

To help users better manage their files and reduce clutter, apps targeting Android 10 (API level 29) and later are granted partitioned access to external storage by default. An application can access only application-specific directories on the external storage space and specific media files created by the application.

The categories of specific storage locations are as follows:

Two, each version of Android file storage targeted adaptation

Because Android partition storage can be said to be a big impact on the previous file system management, so in order to avoid the difficulty of developers access, reduce the pain of adaptation. Android 10 has taken a compromise approach:

Temporarily disable partition storage, particular way is in the application of the listing file requestLegacyExternalStoraged value is set to true.

As follows:

<manifest ... >
<!-- This attribute is "false" by default on apps targeting
Android 10 or higher. -->
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>
Copy the code

Note: if the application in carrying 11 equipment running on Android, the system will ignore requestLegacyExternalStorage properties. So this method can only be used for transitions on Android 10.

This is pretty clear for developers: we just need to target Android 11 and above for file storage and Android 10 and below.

Examples from the file sharing SDK

In the original file sharing SDK, the absolute path of a file to upload a file is required to tell Native SDK, but due to the limitation of Partition storage of Android 11, the file sharing SDK actually has no permission to get the absolute path of the file. At this point, there are two options, as follows:

Solution 1: Copy the file to the application sandbox so that you can obtain the file path

A common code example looks like this:

Public static String getPath(final Context Context) public static String getPath(final Context) String path = getPathForSAF(context, Uri); if (TextUtils.isEmpty(path)) { Log.i(TAG, "getPathForSAF fail uri:" + uri.toString()); // If not, copy a file to the cache directory and return the file path in the cache directory. Path = getPathForCopyFile(context, URI); } return path; }Copy the code

The advantages of this scheme, there is no compatibility problem, out of the box. However, the disadvantages are obvious, such as occupying the storage space of the application, and copying files also consumes time.

Scenario 2: Use file descriptors (best practices for official documents)

1. Run on Android 11

Use the following methods:

Step 1: Request the READ_EXTERNAL_STORAGE permission, following the best practices described in requesting application permission.

Step 2: Use file descriptors to access files.

For more details on how to access Files in native code, see the Files for Miles talk at the 2018 Android Developer Summit (starting at 15:20).

Example code is as follows:

/** * Get the fd of the Uri from the ContentResolver and pass it to the Native SDK for processing. * Remember to call close() after using the file descriptor. * @param uri * @throws FileNotFoundException */ public static int getFd(final Context context, final Uri uri) throws FileNotFoundException { String fileOpenMode = "r"; ParcelFileDescriptor parcelFd = context.getContentResolver().openFileDescriptor(uri, fileOpenMode); if (parcelFd ! = null) { return parcelFd.detachFd(); } return -1; }Copy the code

This solution has the advantage of not copying files, but may have compatibility problems.

2. Run on Android 10 or lower

If your application is targeting Android 10 (API level 29), disable partitioned storage and continue to perform this operation using the method applicable to Android 9 and lower.

Use the following methods:

Step 1: Request the WRITE_EXTERNAL_STORAGE permission, following the best practices described in Requesting Application permission.

Step 2: Use the direct file path to access the file.

Four,

After understanding the principles and best practices of partitioned storage, I believe that you are more confident about Android 10 and beyond. The original intention of “partition storage” is also to build a better Android file system management mechanism, I hope we can embrace the change and build a better tomorrow!