Android8.0 runtime permission policy changes and adaptation scheme

Copyright statement: Reprints must indicate that this article is transferred from Yan Zhenjie's blog: http://blog.yanzhenjie.com

Android 8.0, also known as Android O, is about to be released. There are many new features. At present, we can download the Developer Preview 4 version of the latest Android O system image through the Android Studio 3.0 Canary version. Developer Preview 4 is before the official version of Android O is launched. The last preview release, so it's a release candidate for Android O, and we can use it to complete development and testing and make the transition to Android O smooth for our apps.

An article on Android O behavior changes and compatibility solutions will be planned later. This article focuses on one of the Android O behavior changes— the policy changes and adaptation solutions for system runtime permissions .

The runtime permissions of the Android system have been added since Android 6.0 (Android M). If you don't know the Android runtime permissions, you can read the Android 6.0 runtime permission management best practices :
http://blog.csdn.net /yanzhenjie1003/article/details/52503533

For runtime permission management, there are many open source management libraries. In September 2016, I also open sourced a runtime permission management solution, which is compatible with domestic machines to the greatest extent, and is also compatible with Android 8.0:
https://github. com/yanzhenjie/AndPermission

Before the official start, first correct a problem. I saw on the Internet that there are projects that can customize the application authorization system Dialog. First of all, it is absolutely impossible to correct it. At most, play one of your own before calling the application code. Dialog prompts the user to apply for authorization. I quickly read the source code of the project, and as I imagined, after going around in a circle, I finally called the code of the system to apply for authorization.


Android O's runtime permission policy changes

If you like to read articles on Google's official website, you can read it here:
https://developer.android.com/preview/behavior-changes.html#rmp

Before Android O, if an app requested a permission at runtime and was granted that permission, the system would incorrectly grant the app along with other permissions that belonged to the same permission group and were registered in the manifest.

For apps targeting Android O, this behavior has been corrected. The system will only grant permissions that the app explicitly requests. However, once a user grants a permission to an app, all subsequent requests for permissions in that permission group will be automatically approved.

For example, suppose an app lists READ_EXTERNAL_STORAGEand in its manifest WRITE_EXTERNAL_STORAGE. The app requests READ_EXTERNAL_STORAGEand the user grants the permission, and if the app targets API level 24 or lower, the system also grants WRITE_EXTERNAL_STORAGEit because the permission also belongs to the STORAGEpermission group and is also registered in the manifest. If the app is targeting Android O, the system will only grant it at this time READ_EXTERNAL_STORAGE, but when the app requests the WRITE_EXTERNAL_STORAGEpermission later, the system will grant the permission immediately without prompting the user. (Someone told me in private that this is no different from the original. A discerning person should see that the status of the permission that this group did not apply for should be denied, so what would happen if the permission of the denied status is directly used.)

Let's take READ_EXTERNAL_STORAGEand WRITE_EXTERNAL_STORAGEas an example to analyze in detail, what impact this has on our existing code.

Before the official start, we first agree on two methods:

/**
 * 拿到没有被授权的权限。
 */
getDeinedPermission(String... permissions);
/**
 * 请求几个权限。
 */
requestPermission(String... deinedPermissions);

The constant of the permission is in the Manifest.permissionclass, and the READ_EXTERNAL_STORAGEpermission is added after API 16, so after Android M comes out, in order to adapt to the lower version of the system, we generally apply for the permission (pseudo code) like this:

// 需要申请的权限。
String[] permissions = {
    Manifest.permission.WRITE_EXTERNAL_STORAGE,
    Manifest.permission.READ_SMS,
    ...
};

String[] deniedPermissions = getDeinedPermission(permissions);

if(deniedPermissions.length <= 0) {
    // TODO do something...
} else {
    requestPermission(deniedPermissions, callback);
}

The logic is very simple and clear. The callback is the callback for applying for permission. Here we apply for WRITE_EXTERNAL_STORAGEpermission. Before Android O, we will get READ_EXTERNAL_STORAGEpermission at the same time. When we read the memory card in other places, we only need to judge that WRITE_EXTERNAL_STORAGEwe have permission. to read.

Bat , if the application is installed in the system at this time, we will find that the application crashes when we read the content after Android Ojudging that we have permission, because we did not apply for permission.WRITE_EXTERNAL_STORAGE存储卡READ_EXTERNAL_STORAGE

Response to Android O runtime permission policy changes

According to the characteristics of the runtime permission policy of Android O, in order to adapt to various versions of the system, our code will become as follows (pseudo code):

// 需要申请的权限。
String[] permissions = {
    Manifest.permission.WRITE_EXTERNAL_STORAGE,
    Manifest.permission.READ_EXTERNAL_STORAGE,
    Manifest.permission.READ_SMS,
    ...
};

String[] deniedPermissions = getDeinedPermission(permissions);

if(deniedPermissions.length <= 0) {
    // TODO do something...
} else {
    requestPermission(deniedPermissions, callback);
}

However, there are two problems in this way. First, some permission groups have more permissions, and it is difficult for developers to remember them all. Second, READ_EXTERNAL_STORAGEthis permission constant was added to the SDK when API 16 was released. Similar permission constants are also available. Several, some were even added to the SDK with Android M. If we force it to be written, the app will still crash when running on a lower version of the system. Some people said that we should judge the system version before applying. Of course, if you don't mind the trouble, that's perfectly fine.

Upgrade plan

Therefore, we have concluded a better solution . In the final analysis, it is necessary to apply for a permission group when applying for permission, rather than a single permission. Therefore, we classify according to the system permission group, put the constants of a group into an array, and assign values ​​to this array according to the system version, so we generate such a class:

public final class Permission {

    public static final String[] CALENDAR;   // 读写日历。
    public static final String[] CAMERA;     // 相机。
    public static final String[] CONTACTS;   // 读写联系人。
    public static final String[] LOCATION;   // 读位置信息。
    public static final String[] MICROPHONE; // 使用麦克风。
    public static final String[] PHONE;      // 读电话状态、打电话、读写电话记录。
    public static final String[] SENSORS;    // 传感器。
    public static final String[] SMS;        // 读写短信、收发短信。
    public static final String[] STORAGE;    // 读写存储卡。

    static {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            CALENDAR = new String[]{};
            CAMERA = new String[]{};
            CONTACTS = new String[]{};
            LOCATION = new String[]{};
            MICROPHONE = new String[]{};
            PHONE = new String[]{};
            SENSORS = new String[]{};
            SMS = new String[]{};
            STORAGE = new String[]{};
        } else {
            CALENDAR = new String[]{
                    Manifest.permission.READ_CALENDAR,
                    Manifest.permission.WRITE_CALENDAR};

            CAMERA = new String[]{
                    Manifest.permission.CAMERA};

            CONTACTS = new String[]{
                    Manifest.permission.READ_CONTACTS,
                    Manifest.permission.WRITE_CONTACTS,
                    Manifest.permission.GET_ACCOUNTS};

            LOCATION = new String[]{
                    Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_COARSE_LOCATION};

            MICROPHONE = new String[]{
                    Manifest.permission.RECORD_AUDIO};

            PHONE = new String[]{
                    Manifest.permission.READ_PHONE_STATE,
                    Manifest.permission.CALL_PHONE,
                    Manifest.permission.READ_CALL_LOG,
                    Manifest.permission.WRITE_CALL_LOG,
                    Manifest.permission.USE_SIP,
                    Manifest.permission.PROCESS_OUTGOING_CALLS};

            SENSORS = new String[]{
                    Manifest.permission.BODY_SENSORS};

            SMS = new String[]{
                    Manifest.permission.SEND_SMS,
                    Manifest.permission.RECEIVE_SMS,
                    Manifest.permission.READ_SMS,
                    Manifest.permission.RECEIVE_WAP_PUSH,
                    Manifest.permission.RECEIVE_MMS};

            STORAGE = new String[]{
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE};
        }
    }

}

Before Android M, user authorization is not required to use a certain permission. You only need to register in the Manifest. After Android M, you need to register and apply for user authorization. Therefore, we use an empty array as the permission group before Android M according to the system version. Use real array permissions after Android M.

Because multiple permission groups need to be passed in, the two methods we agreed on are not enough, so we add two methods:

/**
 * 拿到没有被授权的权限。
 */
String[] getDeinedPermission(String... permissions);
/**
 * 请求几个权限。
 */
void requestPermission(String... deinedPermissions);
/**
 * 拿到没有被授权的权限。
 */
String[] getDeinedPermission(String[]... permissions);
/**
 * 请求几个权限。
 */
void requestPermission(String[]... deinedPermissions);

So the code we apply for permission is simplified to this:

// 这方法里面判断版本,返回空数组或者没有权限的数组。
String[] deniedPermissions = getDeinedPermission(Permission.STORAGE, Permission.SMS);

if(deniedPermissions.length <= 0) {
    // TODO do something...
} else {
    requestPermission(deniedPermissions, callback);
}

Of course, this is not the most simplified, but it is enough to be compatible with the changes in Android O's permission policy.

This is the end of the introduction to Android O's runtime permission policy changes and solutions. If you still don't understand, you can leave a message at the bottom of the blog.


Copyright statement: Reprints must indicate that this article is transferred from Yan Zhenjie's blog: http://blog.yanzhenjie.com

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325170371&siteId=291194637