Android storage evolution: partitioned storage

One-partition storage overview

Before Android 10, Android’s file storage phenomenon was like a trash can. Once an app has access to the storage space WRITE_EXTERNAL_STORAGE, it can create files arbitrarily, which is difficult to manage. The user experience is also very poor. When you open the file manager, you will find that it is impossible to find a specific file.

1.1 Principles of Partition Storage

In order to better manage your files and reduce confusion, and strengthen privacy protection, Android Q began to introduce a partitioned storage mechanism. The external storage space has been redesigned and divided by application private and public sharing. Applications can only access their own private space, or access shared resource directories through MediaStore API and Storage Access Framework .

Partition storage mainly follows three major principles to redesign file storage:

  1. Record the source of the file: The system will record which application the file was created by, and the application can read and write the file created by itself without permission;

    The MediaStore database adds a owner_package_namefield to record which application the file belongs to. After the application is uninstalled, the owner_package_namefield will be blank. That is to say, after uninstalling and reinstalling, the previously created file is no longer created by the application and requires relevant storage permissions to read and write again.

  2. Application data protection: access restrictions on external storage space, applications can only access their own private space or shared space , even if they have access to read and write permissions, they cannot access the private space of other applications;

  3. User data protection: When users download some files, such as email attachments with sensitive information, these files should not be visible to other applications. Added access restrictions for pdf, office, doc and other files. Even if users apply for storage permissions, they cannot access pdf, office, doc and other files created by other applications through MediaStore. They need to be selected by the user through the Storage Access Framework framework. access permission

The newer version of the Android system, it is more dependent on the use of file instead of the position to determine the application access to the file

1.2 Compatibility and judgment about storage methods

  1. When targetSdk <= 28, the application uses the traditional storage method;
  2. When targetSdk <= 29, you can android:requestLegacyExternalStorage="true"turn off the partition storage function by adding it to the application tag of the application manifest , and continue to use the traditional access method.
  3. When targetSdk>>=30, Android will enforce partition storage and cannot be closed.
  4. You can Environment.isExternalStorageLegacy()judge the running mode of the application storage, true means running in a traditional compatible way, false means running in partitioned storage

Note : When the value of the requestLegacyExternalStorage attribute is modified, the old APK must be uninstalled before the reinstallation will take effect

Second, the impact of storage space

Android provides two types of physical storage locations : internal storage space and external storage space . On most devices, the internal storage space is smaller than the external storage space. However, the internal storage space on all devices is always available, so it is more reliable when storing data that applications depend on.

Removable volumes (such as SD cards) are external storage spaces in the file system. The space is large, and current smart phones are basically equipped, but for compatibility, you can also check whether the space is available when using the relevant API.Environment.getExternalStorageState()

// 是否可读写
fun isExternalStorageWritable(): Boolean {
    return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
}

// 是否可读
fun isExternalStorageReadable(): Boolean {
     return Environment.getExternalStorageState() in
        setOf(Environment.MEDIA_MOUNTED, Environment.MEDIA_MOUNTED_READ_ONLY)
}

Before writing to storage, you need to know how much space is available on the device, and an exception will be thrown if it is not enough. However, the memory of smart devices is relatively large now. For this part, you can refer to Google to query the available space.

2.1 Internal storage space

When you open the Device File Explorer of Android studio, you can see the internal space directory of the application: /data/data/package name/

The internal storage space itself is designed to protect application privacy. This part is unchanged . Applications do not need any system permissions to read and write files in these directories. Other apps cannot access files stored in the internal storage space.

The internal storage space provides a directory for the application. One directory is designed for the persistent files of the application, and the other directory contains the cached files of the application. The internal storage space is exclusive to the application, and Filerelated APIs can be used normally , so you can play freely as long as you get the path:

  1. Persistent file root directory File:, context.filesDir()/data/data/package name/files/

  2. Cache file root directory File:: context.cacheDir(), /data/data/package name/cache/

Android also provides some simple APIs to create and delete files: context.openFileOutput(filename, Context.MODE_PRIVATE)context.openFileInput(filename)context.fileList()context.getDir(dirName, Context.MODE_PRIVATE)context.delefteFile(fileName)

Note: After uninstalling the app, the system will automatically remove these directories to free up space! !

2.2 External storage space

/storage/emulated/0/Android/data/包名

The partition storage feature of Android 10 redesigns the external storage space of the Android system. The external storage is divided into two parts: application private directory and shared directory:

  1. Application private directory: store application private data, external storage application private directory corresponds to Android/data/package name
  2. Shared directory: Store files accessible to other applications, including media files, document files, and other files, corresponding to directories such as DCIM, Pictures, Alarms, Music, Notifications, Podcasts, Ringtones, Movies, Download, etc.

2.2.1 Application private space

The same as before, access to the private space of the application under its own external storage does not require any permissions. Like the internals, there is also a directory designed for persistent files of the application, and another directory contains the cached files of the application. FileRelated APIs can also be used normally , so you can play freely as long as you get the path.

The difference that needs to be noted is: After the partition storage feature is turned on, the application can only access its own private space, even if it has the storage permission, it cannot access the private space of other applications.

In addition, the difference with the internal space is that the external storage space may be removed or there may be more than one, so what is returned is an array, and the first element in the returned array is regarded as the main external storage volume. Unless the volume is full or unavailable, use the volume.

  1. Persistent files: getExternalFilesDirs(@NonNull Context context, @Nullable String type)According to the file type, type can be transferred to the sub-directory constants predefined by the system , such as pictures Environment.DIRECTORY_PICTURES, which will be returned at this time /storage/emulated/0/Android/data/包名/files/Pictures. Or pass null and return directly/storage/emulated/0/Android/data/包名/files
  2. Cache ContextCompat.getExternalCacheDirs(context)documents: ,/storage/emulated/0/Android/data/包名/cache

Note: After uninstalling the app, the system will automatically remove these directories to free up space! !

The impact of three shared storage space

If user data is or should be accessible to other apps, and it can be saved even after the user uninstalls the app, use shared storage.

Shared file types, including media files, document files and other files, corresponding to the device DCIM, Pictures, Alarms, Music, Notifications, Podcasts, Ringtones, Movies, Download and other directories. Android respectively provides APIs for obtaining Uri of this type of sharable data file:

  • Media content : MediaStoreThis content can be accessed using API
  • Documents and other files : The system has a special directory to contain other file types, such as PDF documents and books in EPUB format. Applications can use to Storage Access Frameworkaccess these files.

For shared files,. In the past, it was possible to data columnobtain the path and then use the File API to operate, but now it will return failure. After the partition storage feature is enabled, the application can only request the corresponding file from the system through the api provided by the system Uri, and read and write the file through Urigeneration FileDescriptorand InputStreamother methods: (In short, for the addition, deletion, and modification of shared files, the main The problem is the acquisition of Uri )

Note: Android 11 also allows access via path, and the system will automatically redirect to Uri.

val resolver = applicationContext.contentResolver
               //读
                resolver.openFileDescriptor(content-uri, "r")?.use { pfd ->
                    val inputStream =  FileInputStream(pfd.fileDescriptor)
                }
                resolver.openInputStream(content-uri).use { stream ->
                }

                //写
                resolver.openFileDescriptor(content-uri, "w")?.use { pfd ->
                    val outputStream =  FileOutputStream(pfd.fileDescriptor)
                }
                resolver.openOutputStream(content-uri).use { stream ->
                }

                //图片bitmap
                BitmapFactory.decodeFileDescriptor(pfd.fileDescriptor)

3.1 MediaStore API

For the addition, deletion, and modification of MediaStrore API, please refer to the official Google guide . The corresponding uri is mainly obtained through contentResolver, which will not be introduced here. Image Source

3.1.1 Overview of MediaStore

The Android system will automatically scan the external storage space and add media files according to their types to the pre-defined collections of Images, Videos, Audio files, and Downloaded files . Android Q accesses corresponding shared directory file resources through MediaStore.Images, MediaStore.Video, MediaStore.Audio, MediaStore.Downloads. The directories corresponding to the predefined collections are shown in the following table:

media type Hate Create directory by default Allow directory creation
Image content://media/external/images/media Pictures DCIM,Pictures
Audio content://media/external/audio/media Music Alarms,Music,Notifications,Podcasts,Ringtones
Video content://media/external/video/media Movies DCIM,Movies
Download content://media/external/downloads Download Download

Note: It MediaStore.Downloads.EXTERNAL_CONTENT_URIis a new API for Android 10 version, used to create and access non-media files

3.1.1 Changes in MediaStore

  1. MediaStore API creates files in the specified directory of the shared directory or accesses applications to create files by themselves, without applying for storage permissions;
  2. MediaStore API accesses media files (pictures, audios, videos) created in shared directories by other applications. You need to apply for storage permissions. If you don’t apply for storage permissions, the file Uri cannot be queried through ContentResolver, even if the file Uri is obtained by other means, read or Creating a file will throw an exception;
  3. The MediaStore API cannot access non-media files (pdf, office, doc, txt, etc.) created by other applications. The only way to access non-media files created by other applications in Android 10 is to use the Storage Access Framework. Document selector provided.

3.1.2 Where are the files created through api stored? How to customize the location?

When MediaStore APIcreating a file, the file will be saved to the corresponding type directory by default. For example, when the picture is saved in the Pictures/directory, you can go up to view the default directory and allowable directory of the table;

You can use MediaStore.xxx.Media.RELATIVE_PATHthe directory or subdirectory you specify to store, such as:, the contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/自定义子目录")file will be placed Pictures/自定义子目录/in; or use contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment. DIRECTORY_DCIM), put the file DCIM/in

Note: Each type has a corresponding directory that can be created, otherwise it will return a failure. You can view the table above for specific directories that can be created

3.2 Storage Access Framework

The SAF framework allows users to interact with system selectors to select document providers and specific documents and other files for your application to create, open, or modify. Since users are involved in the selection of files, this mechanism does not require any system permissions.

Application by calling ACTION_CREATE_DOCUMENT, ACTION_OPEN_DOCUMENTand ACTION_OPEN_DOCUMENT_TREEIntent to obtain documents Document provider provides and receives the selected file is returned in onActivityResult interface Uri. In addition, when configuring the intent, you should specify the file name and MIME type, and you can also use EXTRA_INITIAL_URIintent extra to specify the URI of the file or directory that the file selector should display when it is first loaded as needed .

This part is also unchanged, you can refer to the official guide: Accessing documents and other files from shared storage

3.2.1 Obtaining persistent permissions

For the uri permissions obtained through the SAF framework, you can apply for persistent permissions without having to re-request each time the phone is restarted.

contentResolver.takePersistableUriPermission(
    documentUri,
    Intent.FLAG_GRANT_READ_URI_PERMISSION
)

Overview of the differences between Android versions of the four storage features

4.1 Other changes: picture location information

Some photos include location information in their metadata so that users can see where the photos were taken. Since this location information is sensitive information, if the app uses partitioned storage, Android 10 will hide this information from the app by default.

If the application needs to access the location information of the photo:

  1. Request ACCESS_MEDIA_LOCATIONpermission in app manifest
  2. By calling setRequireOriginal(), from MediaStoreacquiring objects exact byte photos, and pictures of the incoming URI

Five updates

5.1 Partition storage "bug" of Android 10

Android 10 deletes a media file through the MediaStore API, but simply removes the index of the MediaStore database, but does not actually delete the physical file on the physical storage, and as long as the phone restarts, the index is added again. issue

This requirement is also relatively rare, but it just happened to be discovered by testing. I checked it online and found that this problem does exist, and Android 11 can be deleted normally. If there is any solution, please point it out! !

5.2 Android 11 storage changes

5.2.1 Allow to continue to use the original file path

The file path can be used again, the system automatically redirects to Uri

5.2.2 Add batch operation

In Android 10, the application must obtain the user's confirmation one by one when requesting to edit or delete each file in the MediaStore. In Android 11, applications can request to modify or delete multiple media files at once.

Mainly through the following new batch operation api

method Description
MediaStore.createDeleteRequest (resolver, uris) Batch delete (not put in the recycle bin)
MediaStore.createFavoriteRequest(resolver, uris) Batch collection
MediaStore.createTrashRequest (resolver, uris) Move to the recycle bin in bulk
MediaStore.createWriteRequest(resolver, uris) Get write permissions in batches
val uris = ...
val pi = MediaStore.createWriteRequest(contentResolver,
        uris)
startIntentSenderForResult(pi.intentSender, REQUEST_CODE, null, 0, 0, 0)

//相应
override fun onActivityResult(xxx) {
    when (requestCode) {
        REQUEST_CODE ->
            if (resultCode == Activity.RESULT_OK) {
                //获得权限,继续操作
            } else {
                // 用户拒绝了权限授予
            }
    }
}

Recommended learning video

The secret of Android R partition storage

If you want to learn more about Android-related knowledge points, you can click into my [GitHub] project, which records many Android knowledge points.


Android fans technical exchange and learning group
Insert picture description here

Guess you like

Origin blog.csdn.net/u012165769/article/details/113886933