1. Introduction

Everyone should have had such experience, mobile phone with the use of the inside is filled with all kinds of folders and files do not understand. Even folders with deleted software still exist. Why does this happen? The absence of Google has led to the wild growth of the Android ecosystem, resulting in many development specifications have not been fully implemented. In order to solve this problem, Google decided to take a big step forward with Scoped Storage, also known as sandbox Storage.

So what is a sandbox storage mechanism? The sandbox mechanism is a security mechanism that prevents applications from reading data from other applications.

  1. Each application has its own storage space.
  2. An application cannot flip through its own directory to access the public directory.
  3. All data requested by the application passes permission checks and is not allowed if it does not meet the requirements.

2. Partitioning mechanism on Android10

By default, applications running Android 10 (API level 29) or later are granted the partition access permission (partition storage) to external storage devices. The external storage file access mode is redesigned to facilitate user management of external storage files. If not, it will run in compatibility mode. Compatibility mode is the same as before, and files can be stored directly according to the path.

Can only see the application exclusive directory (through the Context. GetExternalFilesDir () access), and certain types of media. Unless your application needs to access files that reside in an application’s proprietary directory and outside of MediaStore, it is best to use partitioned storage.

When Android10 was released, it was made clear that in 2020, the major platform versions will require all apps to use partitioned storage, regardless of the target SDK level. Therefore, you should make sure that your application can use partitioned storage in advance. To do this, make sure this behavior is enabled for devices running Android 10 (API level 29) and later. Translate into popular language, whether using requestLegacyExternalStorage = true way to compatible mode or reduce targetSDK can in the next 2020 years the Android API (29) 10 exemptions in the update.

So in order to the stability of the application, it should be adapted.

3. Storage permissions for specific partitions

By default, access is restricted to partitioned storage for applications with targetSdkVersion greater than or equal to 29. This application does not require storage-related user permissions, that is, it can view the following types of files in the external storage:

  1. Files in a specific directory outside the application (usegetExternalFilesDir()Access).
  2. The app creates its own photos, videos, and audio (viaMediaStoreAccess).

Partitioned storage will affect applications that are first installed on Android10 and targetSdkVersion >=29. Applications that need to access and share files on external storage will be affected. Therefore, compatibility and adaptation must be implemented.

TargetSdkVersion <= 28, not affected 2. If targetSdkVersion >= 29, the external storage visibility of the application is filtered by default and the application needs to adapt the partitioned storage.

It is also worth noting that the following two cases are special and not affected by partitioned storage:

If the app is installed first on a system under Android 10,

1) The system is then upgraded to Android 10 via Fota 2) the application is updated to targetSdkVersion >= 29

Below is a table about partition storage permissions and other related items.

type location Access files generated by the application itself Access files generated by other applications Access methods Uninstalling the application whether to delete files
External storage Photo/ Video/ Audio/ Without permission The permission READ_EXTERNAL_STORAGE is required MediaStore Api no
External storage Downloads Without permission Without permission Load the system file selector through the storage access framework SAF no
External storage Apply a specific directory Without permission No direct access GetExternalFilesDir () gets the path to the application’s own files is

4. Private directory storage

An application does not need to obtain storage permissions when it reads or writes files in a directory that should be private. Want to get the current application in the application of proprietary storage directory path is can use the Context getExternalFilesDir get ().

val dirpath = context.getExternalFilesDir("")
val fileString = dirpath + File.separator
val file = File(fileString)
...    // The remaining steps are to use Java IO or other IO libraries to write data
Copy the code

5. Share media set storage

When saving media files in the shared media set store, you need to select MediaStore according to the file type. Put the relevant data into ContentValues, and finally insert ContentValues into the ContentResolver and get the Uri returned. OutputStream is passed through Uri, and the file is stored using Okio’s IO library. The thing about Okio is that we’ll talk about it when we get a chance. Don’t forget to get permissions here.

// Download the image to the public media collection and display it in the album
// Create ContentValues and add the information
val values = ContentValues()
values.put(MediaStore.Images.Media.DESCRIPTION, downloadedFile.name)
values.put(MediaStore.Images.Media.DISPLAY_NAME, downloadedFile.name)
values.put(MediaStore.Images.Media.MIME_TYPE, mimeType)
values.put(MediaStore.Images.Media.TITLE, downloadedFile.name)
values.put(
    MediaStore.Images.Media.RELATIVE_PATH,
    "${Environment.DIRECTORY_PICTURES}/${downloadedFile.name}"
)
// Insert into the ContentResolver and return the Uri
val insertUri = context.contentResolver.insert(
    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
    values
)

if(insertUri ! =null) {
    / / get the OutputStream
    val outputStream = context.contentResolver.openOutputStream(insertUri)
if(outputStream ! =null) {
    sink = outputStream.sink().buffer()
} else {
    return@runCatching FileDownloadResult.OthersError
    }
} else {
    return@runCatching FileDownloadResult.OthersError
}

 valresponseBody = response.body ? :return@runCatching FileDownloadResult.OthersError

try {
    val contentLength = responseBody.contentLength()
    if (contentLength > FileUtil.getAvailableSize(dirPath)) {
        continuation.resume(FileDownloadResult.StorageError)
    }
    var totalRead: Long = 0
    var lastRead: Long

    do {
        lastRead = responseBody.source().read(sink.buffer(), BUFFER_SIZE)
        if (lastRead == - 1L) {
            break
        }
        totalRead += lastRead
        sink.emitCompleteSegments()
    } while (true)
    sink.writeAll(responseBody.source())
    sink.close()
    responseBody.close()
}
Copy the code

6. Other

Making: github.com/HyejeanMOON…

Android Jetpack Room: juejin.cn/post/684490… Android Property Animation tutorial: juejin.cn/post/684490… Android ConstraintLayout: juejin.cn/post/684490… In RecyclerView can cope with multiple ViewType libraries -Groupie: juejin.cn/post/684490… Use of Google MergeAdapter: juejin.cn/post/684490… Paging in Android: juejin.cn/post/684490… Android UI testing Espresso: juejin.cn/post/684490… Android WorkManager: juejin.cn/post/684490…