Android M 新的运行时权限开发者需要知道的一切

原文出处:http://jijiaxin89.com/2015/08/30/Android-s-Runtime-Permission/

PROTECTION_NORMAL类权限

当用户安装或更新应用时,系统将授予应用所请求的属于 PROTECTION_NORMAL 的所有权限(安装时授权的一类基本权限)。这类权限包括:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
android.permission.ACCESS_LOCATION_EXTRA_COMMANDS
android.permission.ACCESS_NETWORK_STATE
android.permission.ACCESS_NOTIFICATION_POLICY
android.permission.ACCESS_WIFI_STATE
android.permission.ACCESS_WIMAX_STATE
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
android.permission.BROADCAST_STICKY
android.permission.CHANGE_NETWORK_STATE
android.permission.CHANGE_WIFI_MULTICAST_STATE
android.permission.CHANGE_WIFI_STATE
android.permission.CHANGE_WIMAX_STATE
android.permission.DISABLE_KEYGUARD
android.permission.EXPAND_STATUS_BAR
android.permission.FLASHLIGHT
android.permission.GET_ACCOUNTS
android.permission.GET_PACKAGE_SIZE
android.permission.INTERNET
android.permission.KILL_BACKGROUND_PROCESSES
android.permission.MODIFY_AUDIO_SETTINGS
android.permission.NFC
android.permission.READ_SYNC_SETTINGS
android.permission.READ_SYNC_STATS
android.permission.RECEIVE_BOOT_COMPLETED
android.permission.REORDER_TASKS
android.permission.REQUEST_INSTALL_PACKAGES
android.permission.SET_TIME_ZONE
android.permission.SET_WALLPAPER
android.permission.SET_WALLPAPER_HINTS
android.permission.SUBSCRIBED_FEEDS_READ
android.permission.TRANSMIT_IR
android.permission.USE_FINGERPRINT
android.permission.VIBRATE
android.permission.WAKE_LOCK
android.permission.WRITE_SYNC_SETTINGS
com.android.alarm.permission.SET_ALARM
com.android.launcher.permission.INSTALL_SHORTCUT
com.android.launcher.permission.UNINSTALL_SHORTCUT

只需要在AndroidManifest.xml中简单声明这些权限就好,安装时就授权。不需要每次使用时都检查权限,而且用户不能取消以上授权。

让你的app支持新运行时权限

是时候让我们的app支持新权限模型了,从设置compileSdkVersion andtargetSdkVersion 为 23开始吧.

1
2
3
4
5
6
7
8
9
android {
     compileSdkVersion 23
     ...
  
     defaultConfig {
         ...
         targetSdkVersion 23
         ...
     }

例子,我想用一下方法添加联系人。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private static final String TAG =  "Contacts" ;
private void insertDummyContact() {
     // Two operations are needed to insert a new contact.
     ArrayList operations =  new  ArrayList(2);
  
     // First, set up a new raw contact.
     ContentProviderOperation.Builder op =
             ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
                     .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE,  null )
                     .withValue(ContactsContract.RawContacts.ACCOUNT_NAME,  null );
     operations.add(op.build());
  
     // Next, set the name for the contact.
     op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
             .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
             .withValue(ContactsContract.Data.MIMETYPE,
                     ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
             .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
                     "__DUMMY CONTACT from runtime permissions sample" );
     operations.add(op.build());
  
     // Apply the operations.
     ContentResolver resolver = getContentResolver();
     try  {
         resolver.applyBatch(ContactsContract.AUTHORITY, operations);
     catch  (RemoteException e) {
         Log.d(TAG,  "Could not add a new contact: "  + e.getMessage());
     catch  (OperationApplicationException e) {
         Log.d(TAG,  "Could not add a new contact: "  + e.getMessage());
     }
}

上面代码需要WRITE_CONTACTS权限。如果不询问授权,app就崩了。
下一步像以前一样在AndroidManifest.xml添加声明权限。

1
"android.permission.WRITE_CONTACTS" />

下一步,不得不再写个方法检查有没有权限。如果没有弹个对话框询问用户授权。然后你才可以下一步创建联系人。
权限被分组了,如下表:

Android M 新的运行时权限开发者需要知道的一切_第1张图片

同一组的任何一个权限被授权了,其他权限也自动被授权。例如,一旦WRITE_CONTACTS被授权了,app也有READ_CONTACTS和GET_ACCOUNTS了。
源码中被用来检查和请求权限的方法分别是Activity的checkSelfPermission和requestPermissions。这些方法api23引入。

1
2
3
4
5
6
7
8
9
10
11
final private int REQUEST_CODE_ASK_PERMISSIONS = 123;
  
private void insertDummyContactWrapper() {
     int hasWriteContactsPermission = checkSelfPermission(Manifest.permission.WRITE_CONTACTS);
     if  (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {
         requestPermissions( new  String[] {Manifest.permission.WRITE_CONTACTS},
                 REQUEST_CODE_ASK_PERMISSIONS);
         return ;
     }
     insertDummyContact();
}

如果已有权限,insertDummyContact()会执行。否则,requestPermissions被执行来弹出请求授权对话框,如下:

Android M 新的运行时权限开发者需要知道的一切_第2张图片

不论用户同意还是拒绝,activity的onRequestPermissionsResult会被回调来通知结果(通过第三个参数),grantResults,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
     switch  (requestCode) {
         case  REQUEST_CODE_ASK_PERMISSIONS:
             if  (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                 // Permission Granted
                 insertDummyContact();
             else  {
                 // Permission Denied
                 Toast.makeText(MainActivity. this "WRITE_CONTACTS Denied" , Toast.LENGTH_SHORT)
                         .show();
             }
             break ;
         default :
             super .onRequestPermissionsResult(requestCode, permissions, grantResults);
     }
}

这就是新权限模型工作过程。代码真复杂但是只能去习惯它。。。为了让app很好兼容新权限模型,你不得不用以上类似方法处理所有需要的情况。
如果你想捶墙,现在是时候了。。。

处理 “不再提醒”

如果用户拒绝某授权。下一次弹框,用户会有一个“不再提醒”的选项的来防止app以后继续请求授权。

Android M 新的运行时权限开发者需要知道的一切_第3张图片

如果这个选项在拒绝授权前被用户勾选了。下次为这个权限请求requestPermissions时,对话框就不弹出来了,结果就是,app啥都不干。
这 将是很差的用户体验,用户做了操作却得不到响应。这种情况需要好好处理一下。在请求requestPermissions前,我们需要检查是否需要展示请 求权限的提示通过activity的shouldShowRequestPermissionRationale,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
final private int REQUEST_CODE_ASK_PERMISSIONS = 123;
  
private void insertDummyContactWrapper() {
     int hasWriteContactsPermission = checkSelfPermission(Manifest.permission.WRITE_CONTACTS);
     if  (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {
             if  (!shouldShowRequestPermissionRationale(Manifest.permission.WRITE_CONTACTS)) {
                 showMessageOKCancel( "You need to allow access to Contacts" ,
                         new  DialogInterface.OnClickListener() {
                             @Override
                             public void onClick(DialogInterface dialog, int which) {
                                 requestPermissions( new  String[] {Manifest.permission.WRITE_CONTACTS},
                                         REQUEST_CODE_ASK_PERMISSIONS);
                             }
                         });
                 return ;
             }
         requestPermissions( new  String[] {Manifest.permission.WRITE_CONTACTS},
                 REQUEST_CODE_ASK_PERMISSIONS);
         return ;
     }
     insertDummyContact();
}
  
private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
     new  AlertDialog.Builder(MainActivity. this )
             .setMessage(message)
             .setPositiveButton( "OK" , okListener)
             .setNegativeButton( "Cancel" null )
             .create()
             .show();
}

当一个权限第一次被请求和用户标记过不再提醒的时候,我们写的对话框被展示。
后一种情况,onRequestPermissionsResult 会收到PERMISSION_DENIED ,系统询问对话框不展示。

Android M 新的运行时权限开发者需要知道的一切_第4张图片

搞定!

一次请求多个权限

当然了有时候需要好多权限,可以用上面方法一次请求多个权限。不要忘了为每个权限检查“不再提醒”的设置。
修改后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
final private int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124;
  
private void insertDummyContactWrapper() {
     List permissionsNeeded =  new  ArrayList();
  
     final List permissionsList =  new  ArrayList();
     if  (!addPermission(permissionsList, Manifest.permission.ACCESS_FINE_LOCATION))
         permissionsNeeded.add( "GPS" );
     if  (!addPermission(permissionsList, Manifest.permission.READ_CONTACTS))
         permissionsNeeded.add( "Read Contacts" );
     if  (!addPermission(permissionsList, Manifest.permission.WRITE_CONTACTS))
         permissionsNeeded.add( "Write Contacts" );
  
     if  (permissionsList.size() > 0) {
         if  (permissionsNeeded.size() > 0) {
             // Need Rationale
             String message =  "You need to grant access to "  + permissionsNeeded.get(0);
             for  (int i = 1; i < permissionsNeeded.size(); i++)
                 message = message +  ", "  + permissionsNeeded.get(i);
             showMessageOKCancel(message,
                     new  DialogInterface.OnClickListener() {
                         @Override
                         public void onClick(DialogInterface dialog, int which) {
                             requestPermissions(permissionsList.toArray( new  String[permissionsList.size()]),
                                     REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
                         }
                     });
             return ;
         }
         requestPermissions(permissionsList.toArray( new  String[permissionsList.size()]),
                 REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
         return ;
     }
  
     insertDummyContact();
}
  
private boolean addPermission(List permissionsList, String permission) {
     if  (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
         permissionsList.add(permission);
         // Check for Rationale Option
         if  (!shouldShowRequestPermissionRationale(permission))
             return  false ;
     }
     return  true ;
}

如果所有权限被授权,依然回调onRequestPermissionsResult,我用hashmap让代码整洁便于阅读。

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    switch (requestCode) {
        case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS:
            {
            Map perms = new HashMap();
            // Initial
            perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED);
            perms.put(Manifest.permission.READ_CONTACTS, PackageManager.PERMISSION_GRANTED);
            perms.put(Manifest.permission.WRITE_CONTACTS, PackageManager.PERMISSION_GRANTED);
            // Fill with results
            for (int i = 0; i < permissions.length; i++)
                perms.put(permissions[i], grantResults[i]);
            // Check for ACCESS_FINE_LOCATION
            if (perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
                    && perms.get(Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED
                    && perms.get(Manifest.permission.WRITE_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
                // All Permissions Granted
                insertDummyContact();
            } else {
                // Permission Denied
                Toast.makeText(MainActivity.this, "Some Permission is Denied", Toast.LENGTH_SHORT)
                        .show();
            }
            }
            break;
        default:
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}

条件灵活的,你自己设置。有的情况,一个权限没有授权,就不可用;但是也有情况,能工作,但是表现的是有所限制的。对于这个我不做评价,你自己设计吧



转载于:https://my.oschina.net/u/2312644/blog/525392

你可能感兴趣的:(Android M 新的运行时权限开发者需要知道的一切)