New features of Android 11, Scoped Storage has a new trick

This article is simultaneously published on my WeChat official account. Scan the QR code at the bottom of the article or search Guo Lin on WeChat to follow. The article is updated every working day.

It's been more than half a year since Android 11 was officially released, and it's time to write articles about the new features of Android 11.

At the beginning, I probably learned about some behavior changes on Android 11. Although there are a lot of changes in general, there are not many places that require us to adapt. One area that may need to be adapted is the permission change of Android 11. Regarding this part of the content, I now support Java in PermissionX! There is also an explanation of Android 11 permission changes. This article has already done a more detailed explanation.

In addition, in Scoped Storage, Android 11 has some new changes. In this article, we will focus on this part.

Scoped Storage

In fact, Scoped Storage is not a new feature launched on Android 11, but it already exists in Android 10. And I also wrote an article to explain this feature at the time. You can refer to Android 10 adaptation points and scope. Storage .

Don't worry, the content described in the previous article is not out of date. The functions that were available on Android 10 at that time are still available on Android 11, but Android 11 has enriched and expanded Scoped Storage. So there is no doubt that this is the focus of our article.

Force Scoped Storage

First of all, in Android 11, Scoped Storage is forcibly enabled.

So what does it mean to force it?

Although there is also a Scoped Storage function in Android 10, Google considers that it takes time to adapt to a wide range of applications, so this function is not mandatory.

As long as the targetSdkVersion specified by the application is lower than 29, or the targetSdkVersion is equal to 29, but the following configuration is added to AndroidManifest.xml:

<manifest ... >
  <application android:requestLegacyExternalStorage="true" ...>
    ...
  </application>
</manifest>

Then the Scoped Storage function will not be enabled.

The above configuration is still valid in Android 11, but only when the targetSdkVersion is less than or equal to 29. If your targetSdkVersion is equal to 30, Scoped Storage will be forcibly enabled, and the requestLegacyExternalStorage flag will be ignored.

So is there any impact on developers after enabling Scoped Storage?

In fact, if your application has been adapted to Scoped Storage in the way explained in this article, according to the Android 10 adaptation points, Scoped Storage, then congratulations, now you don’t need to do anything, you can already adapt to Android 11 systems.

In other words, for most developers, forcing Scoped Storage to be enabled has no effect, as long as your application has been adapted to Android 10 Scoped Storage before.

But there is a very special type of application, that is, file browsers, such as Root Explorer, ES Explorer, etc. The function provided by this kind of program itself is to browse and manage files on SD, and after Scoped Storage is forcibly enabled, there is essentially no concept of file browsing, and we cannot manage files with their real paths.

From this perspective, Scoped Storage has caused a devastating blow to file browser programs. But don't worry, Google still provides another solution for this kind of program, let's learn about it below.

Manage all files on the device

First of all, make it clear that the mandatory enabling of Scoped Storage in Android 11 is to better protect user privacy and provide more secure data protection. For most applications, the API provided by MediaStore can already meet your development needs. If you don't have a requirement similar to developing a file browser, please try not to use the technology that will be introduced next.

Having read and write permissions for the entire SD card is considered a very dangerous permission on Android 11. It may also have a greater impact on the user's data security.

But the file browser is to manage the entire SD card of the device. What should I do? For such highly dangerous permissions, Google usually uses Intent to jump to a special authorization page to guide users to manually authorize, such as floating windows, accessibility services, etc.

Yes, in Android 11, if you want to manage files on the entire device, you also need to use similar techniques.

First, you must declare the MANAGE_EXTERNAL_STORAGE permission in AndroidManifest.xml, as shown below:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.scopedstoragedemo">

    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
        tools:ignore="ScopedStorage" />

</manifest>

Note that compared to the traditional declaration of a permission, an attribute such as tools:ignore="ScopedStorage" is added here. Because if you don't add this attribute, Android Studio will remind us with a warning that most applications should not apply for this permission, as I introduced earlier.

The next work is also quite simple. We can use the action ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION to jump to the specified authorization page. You can use the Environment.isExternalStorageManager() function to determine whether the user is authorized. Below I write a relatively simple code to demonstrate This function:

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R ||
        Environment.isExternalStorageManager()) {
    
    
    Toast.makeText(this, "已获得访问所有文件权限", Toast.LENGTH_SHORT).show()
} else {
    
    
    val builder = AlertDialog.Builder(this)
        .setMessage("本程序需要您同意允许访问所有文件权限")
        .setPositiveButton("确定") {
    
     _, _ ->
            val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
            startActivity(intent)
        }
    builder.show()
}

It can be seen that if the system version is lower than Android 11, or Environment.isExternalStorageManager() returns true, it means that we already have the authority to manage the entire SD card. Now you can directly use the traditional way of writing to manipulate files in the form of their real paths.

And if there is no permission to manage the SD card, a dialog box will pop up to inform the user of the reason for applying for permission, and then use Intent to jump to the specified authorization page and let the user manually authorize.

The running effect of the program is shown in the figure below:

With this permission, you can develop the file browser in the way you know it in the past.

But there is one more thing to note. Even if we have the permission to manage the SD card, many resources in the Android directory are still restricted. For example, the Android/data directory cannot be accessed by any means in Android 11. Because the data information of many applications will be stored in this directory, the purpose of this restriction is mainly to consider the user's data security. Otherwise, it seems inappropriate to allow WeChat to read data in Taobao.

Batch operations

Let's look at another new feature of Scoped Storage in Android 11.

Scoped Storage stipulates that each application has the right to contribute data to MediaStore, such as inserting a picture into a mobile phone album. It also has permission to read data contributed by other applications, such as obtaining all the pictures in the phone album. These features are demonstrated in this article on Android 10 Adaptation Points and Scope Storage.

However, if you want to modify the data contributed by other applications, sorry, Scoped Storage does not allow you to do so.

The reason is also very simple. If you insert a picture into your phone album, you certainly have the authority to modify it arbitrarily. But if this picture is inserted into the phone album by another application, you can modify it arbitrarily. This is another security risk in Google's view, so Scoped Storage restricts this function.

However, what if some applications just need to modify the data contributed by other applications? Such examples are not difficult to find, such as Photoshop, Meitu Xiuxiu, etc. Their purpose is to modify the pictures in the mobile phone album, regardless of whether the pictures are created by themselves.

To solve this problem, Android 10 provides a solution:

try {
    
    
    contentResolver.openFileDescriptor(imageContentUri, "w")?.use {
    
    
        Toast.makeText(this, "现在可以修改图片的灰度了", Toast.LENGTH_SHORT).show()
    }
} catch (securityException: SecurityException) {
    
    
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    
    
        val recoverableSecurityException = securityException as?
            RecoverableSecurityException ?:
            throw RuntimeException(securityException.message, securityException)

        val intentSender = recoverableSecurityException.userAction.actionIntent.intentSender
        intentSender?.let {
    
    
            startIntentSenderForResult(intentSender, IMAGE_REQUEST_CODE,
                    null, 0, 0, 0, null)
        }
    } else {
    
    
        throw RuntimeException(securityException.message, securityException)
    }
}

Let me briefly explain this code.

First of all, the purpose of this code is to modify the grayscale of a picture, but since this picture is not contributed by the current application, theoretically the current application does not have the authority to modify the grayscale of this picture.

So obviously we don't have permission to modify, but what happens if we insist on modifying? This is easy to understand, and of course an exception is thrown. So here is a try catch method to wrap the image grayscale operation, and then judge in the catch code block, if the current system version is greater than or equal to Android 10, and the type of exception is RecoverableSecurityException, then it means that this is due to Scoped Storage Restrictions result in abnormal operations without permission.

Next, we will get an intentSender from the RecoverableSecurityException object, and then use this intentSender to jump to the page, and guide the user to manually grant us the permission to modify this picture. The operation effect is as follows:

Although this method is feasible, it has a very obvious disadvantage: we can only manipulate one picture at a time. If a program needs to modify many pictures, there is no good way. You can only apply for permission for each picture in the above way.

I believe that Google is also aware of this problem, so it introduced a new feature called Batch operations in Android 11, which allows us to apply for multiple file operation permissions at once.

The usage of Batch operations is also well understood. Google provides a total of 4 types of permission application APIs, as shown below:

  • createWriteRequest() is used to request write permission to multiple files.
  • createFavoriteRequest() is used to request the permission to add multiple files to Favorites.
  • createTrashRequest() is used to request permission to move multiple files to the recycle bin.
  • createDeleteRequest() is used to request permission to delete multiple files.

The two most commonly used interfaces are createWriteRequest() and createDeleteRequest(). Here we take createWriteRequest() as an example.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
    
    
    val urisToModify = listOf(uri1, uri2, uri3, uri4)
    val editPendingIntent = MediaStore.createWriteRequest(contentResolver, urisToModify)
    startIntentSenderForResult(editPendingIntent.intentSender, EDIT_REQUEST_CODE,
            null, 0, 0, 0)
}

The code is very simple. First, we create a collection to store all the file Uris to be applied for permissions in batches, then call the createWriteRequest() function to create a PendingIntent, and then call startIntentSenderForResult to apply for permissions.

Regarding the result of permission application, we can monitor in onActivityResult():

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    
    
    super.onActivityResult(requestCode, resultCode, data)
    when (requestCode) {
    
    
        EDIT_REQUEST_CODE -> {
    
    
            if (resultCode == Activity.RESULT_OK) {
    
    
                Toast.makeText(this, "用户已授权", Toast.LENGTH_SHORT).show()
            } else {
    
    
                Toast.makeText(this, "用户没有授权", Toast.LENGTH_SHORT).show()
            }
        }
    }
}

The running result of the program is shown in the figure below:

The usage of several other APIs is exactly the same, so I won’t repeat the example here.

Seeing this, some friends may say that the APIs provided by Android 10 and Android 11 are completely different. Android 10 relies on the exception capture mechanism to parse the intentSender from RecoverableSecurityException, while Android 11 can directly use the API provided by Batch operations. Create intentSender. Do I need to write two sets of codes for Android 10 and Android 11 in a project to adapt?

This is indeed a headache, and I think it is mainly caused by the unreasonable API design of Google in Android 10. The scheme that relies on the exception catching mechanism cannot be said to be an excellent API design in any case.

But with more thinking later, I found that this is not an unsolvable problem, and the solution is still very simple.

why? Don't forget, Scoped Storage in Android 10 is not mandatory to enable, we can configure requestLegacyExternalStorage flag in AndroidManifest.xml to disable Scoped Storage. In this case, Android 10 does not require adaptation. We only need to use a more scientific and standardized API in Android 11 for Scoped Storage adaptation.

Well, this article ends here. I have written all the code examples in the article as a Demo and put it on GitHub. Friends in need can check it out at the following website:

https://github.com/guolindev/ScopedStorageDemo

In addition, if you want to learn Kotlin and the latest Android knowledge, you can refer to my new book "First Line of Code 3rd Edition" , click here for details .


Pay attention to my technical public account, and there will be high-quality technical articles pushed every working day.

Scan the QR code below on WeChat to follow:

Guess you like

Origin blog.csdn.net/sinyu890807/article/details/113954552