Android6.0运行时权限问题

前言 在 Android 6.0 之后的系统,除了要在 AndroidManifest.xml 文件中声明需要使用的权限,部分危险权限还需要在运行程序的过程中动态向用户申请。
在 AndroidManifest.xml 中申请权限

...

危险权限列表
Permission Group Permissions
CALENDAR READ_CALENDAR \ WRITE_CALENDAR
CAMERA CAMERA
CONTACTS READ_CONTACTS \ WRITE_CONTACTS \ GET_ACCOUNTS
LOCATION ACCESS_FINE_LOCATION \ ACCESS_COARSE_LOCATION
MICROPHONE RECORD_AUDIO
PHONE READ_PHONE_STATE \ CALL_PHONE \ READ_CALL_LOG \ WRITE_CALL_LOG \ ADD_VOICEMAIL \ USE_SIP \ PROCESS_OUTGOING_CALLS
SENSORS BODY_SENSORS
SMS SEND_SMS \ RECEIVE_SMS \ READ_SMS \ RECEIVE_WAP_PUSH \ RECEIVE_MMS
STORAGE READ_EXTERNAL_STORAGE \ WRITE_EXTERNAL_STORAGE
注意事项
  1. 危险权限有分组,在申请分组中的其中一个权限后,可以获得该分组中所有权限的使用权。
  2. 一次可以申请多个不同分组的权限,用户会进行多次授权,假若是同一分组的不同权限,因为如上原因,系统只对同一分组中的第一个权限进行一次用户授权询问。
  3. 假若用户已经同意该权限,再次申请会继续弹出对话框让用户选择,且假若用户拒绝会导致 Activity 摧毁再恢复,使得在 Fragment 中申请的权限没有系统回调。所以最好在申请前进行判断,以防万一。
运行时权限获取 使用 Activity Activity#requestPermissions() 方法要求 min API level >= 23 才能使用,因为这是 Android M 才推出的需求,所以可以使用 support-v4 包中的 ActivityCompat#requestPermissions() 方法替代以做到向下兼容。
代码示例:
public static final int REQEST_CODE = 0; public void example(){ if(Build.VERSION.SDK_INT >= 23){ int checkPermission = ContextCompat.checkSelfPermission(context, Manifest.permission.XXX); if(checkPermission != PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(context,new String[]{Manifest.permission.XXX},REQEST_CODE); return; }else{ //do something } }else{ // do something } }

使用 Fragment 如果使用的是 support-v4 包中的 Fragment,可以直接使用 requestPermissions() 申请权限,否则同上。在这里推荐使用 support 包中的 Fragment 而不使用 SDK 中的,因为 support 包中的 Fragment 会比 SDK 中的功能更多,而且可以自由更新不受系统版本影响。
用户响应后的系统回调 系统会回调 onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) 方法。
  1. requestCode 为申请时传递的值
  2. permissions 为申请的权限
  3. grantResults 为对应权限的获取结果,permissions[i] 对应 grantResults[i]
示例代码:
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults){ switch(requestCode){ case REQEST_CODE:{ if(grantResults[0] == PackageManager.PERMISSION_GRANTED){ // Permission Granted }else{ // Permission Denied } break; } default: super.onRequestPermissionsResult(requestCode, permissions, grantResults); } }

【Android6.0运行时权限问题】需要注意的是,onRequestPermissionsResult() 方法回在 Activity 恢复前调用,所以不可以在该方法中 commit FragmentTransaction,需要使用 commitAllowingStateLoss 替代。

    推荐阅读