Android6.0的SDK,对权限的申请机制发生了一些变化。 在Android6.0之前,用户安装APP时就要为其授予对应的权限,不然程序就无法安装,而6.0之后,我们可以直接安装,并可对应用的权限进行管理,这样极大的保护了用户的隐私,但也给我们开发人员造成了些小麻烦(测试人员经常提单反映说某个功能未实现,其实是把权限关闭造成的)
可以看看Google官方文档,申请权限的最佳做法:https://developer.android.com/training/permissions/best-practices.html#perms-vs-intents
Google将应用的权限分为两类
Normal Permissions:不需要用户授予,可直接申请,如访问网络、手机传感器等权限,此类权限一般不涉及用户隐私。Dangerous Permission:需要用户授予,需要动态申请,如拨打电话、访问SD卡、获取地理位置等权限,此类权限一般都涉及到了用户隐私。Normal Permissions列表
ACCESS_LOCATION_EXTRA_COMMANDS ACCESS_NETWORK_STATE ACCESS_NOTIFICATION_POLICY ACCESS_WIFI_STATE BLUETOOTH BLUETOOTH_ADMIN BROADCAST_STICKY CHANGE_NETWORK_STATE CHANGE_WIFI_MULTICAST_STATE CHANGE_WIFI_STATE DISABLE_KEYGUARD EXPAND_STATUS_BAR GET_PACKAGE_SIZE INSTALL_SHORTCUT INTERNET KILL_BACKGROUND_PROCESSES MODIFY_AUDIO_SETTINGS NFC READ_SYNC_SETTINGS READ_SYNC_STATS RECEIVE_BOOT_COMPLETED REORDER_TASKS REQUEST_INSTALL_PACKAGES SET_ALARM SET_TIME_ZONE SET_WALLPAPER SET_WALLPAPER_HINTS TRANSMIT_IR UNINSTALL_SHORTCUT USE_FINGERPRINT VIBRATE WAKE_LOCK WRITE_SYNC_SETTINGSDangerous Permission列表
group:android.permission-group.CONTACTS permission:android.permission.WRITE_CONTACTS permission:android.permission.GET_ACCOUNTS permission:android.permission.READ_CONTACTS group:android.permission-group.PHONE permission:android.permission.READ_CALL_LOG permission:android.permission.READ_PHONE_STATE permission:android.permission.CALL_PHONE permission:android.permission.WRITE_CALL_LOG permission:android.permission.USE_SIP permission:android.permission.PROCESS_OUTGOING_CALLS permission:com.android.voicemail.permission.ADD_VOICEMAIL group:android.permission-group.CALENDAR permission:android.permission.READ_CALENDAR permission:android.permission.WRITE_CALENDAR group:android.permission-group.CAMERA permission:android.permission.CAMERA group:android.permission-group.SENSORS permission:android.permission.BODY_SENSORS group:android.permission-group.LOCATION permission:android.permission.ACCESS_FINE_LOCATION permission:android.permission.ACCESS_COARSE_LOCATION group:android.permission-group.STORAGE permission:android.permission.READ_EXTERNAL_STORAGE permission:android.permission.WRITE_EXTERNAL_STORAGE group:android.permission-group.MICROPHONE permission:android.permission.RECORD_AUDIO group:android.permission-group.SMS permission:android.permission.READ_SMS permission:android.permission.RECEIVE_WAP_PUSH permission:android.permission.RECEIVE_MMS permission:android.permission.RECEIVE_SMS permission:android.permission.SEND_SMS permission:android.permission.READ_CELL_BROADCASTS可以发现,Dangerous Permission中,所有危险的 Android 系统权限都是一个个权限组,当我们在申请一组权限中的其中一个权限时,会对应申请整个权限组,具体可以查看Google的官方文档,描述的很详细:
https://developer.android.com/guide/topics/security/permissions.html#defining
请求权限的步骤,为了兼容性,建议使用SDK 23版本或以上的v4包,这里介绍的是v4包中的API
1、checkSelfPermission(Context context, String permission): 检查用户是否允许该权限,有则返回0,无则返回-1,该方法在API 23版本才提供,为了兼容性低版本,建议使用v4包中ContextCompat类提供的checkSelfPermission()方法
if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED) { // 有权限 } if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_DENIED) { // 无权限 }2、requestPermissions(Activity activity, String[] permissions, int requestCode): 申请权限,会弹出一个系统对话框向用户申请权限,String[] permissions 传入一个或一组权限,int requestCode为请求码,用于在回调监听中标识权限申请的结果。
ActivityCompat.requestPermissions(context, new String[]{Manifest.permission.CAMERA}, STATE_CODE);3、onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults): 监听权限请求的结果,int requestCode对应requestPermissions()方法中的requestCode请求码,int[] grantResults表示对应权限申请的结果,成功返回0,失败返回-1
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case STATE_CODE: if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 申请成功 } break; } }4、shouldShowRequestPermissionRationale(Activity activity, String permission): 申请权限的解释,该方法只在大于等于6.0的版本起作用,在6.0前的版本只返回false,它的返回结果有以下几种情况
当申请对应权限时被用户拒绝了一次,再次申请对应权限时该方法就会返回true如果用户在第二次时选择了“不再提醒”,则再次请求权限时该方法只会返回false用户在手机权限管理中设置为“禁止”,则该方法返回false if (ActivityCompat.shouldShowRequestPermissionRationale(context, Manifest.permission.CAMERA)) { // 用户再次拒绝时 }1、onRequestPermissionsResult回调失效: 不管权限申请结果成功还是失败,都没有回调onRequestPermissionsResult()方法 需要明白以下几点,这个问题就好解决了:
如果你在一个Fragment页面调用requestPermissions()方法,那么当前Fragment页面的onRequestPermissionsResult()就会被回调。
如果你是通过ActivityCompat.requestPermissions()方法调用的,那么Activity页面的onRequestPermissionsResult()就会被回调。
所以,一般出现onRequestPermissionsResult()没有被回调的情况,很可能是由于在Fragment页面中使用ActivityCompat.requestPermissions()方法请求权限造成的,如果要在Fragment中申请权限,应该使用当前Fragment页面本身来申请。
StackOverFlow里也有相关的解答: http://stackoverflow.com/questions/33169455/onrequestpermissionsresult-not-being-called-in-dialog-fragment
2、checkSelfPermission返回始终为PackageManager.PERMISSION_GRANTED: 把应用运行在6.0系统时,不管用户是否拒绝了这个权限,checkSelfPermission返回的结果一直是PackageManager.PERMISSION_GRANTED。 如果碰到了这样的问题,需要检查你的App目标SDK是否是23以下,也就是targetSdkVersion < 23的时候,但APP却运行在6.0以上的机器时,就会出现这样的情况,对于targetSdkVersion < 23的应用,在6.0的机器运行时,我们需要使用PermissionChecker类中的checkSelfPermission()方法,才能正常获取,所以我们在检查用户是否授予权限时,需要进行两套处理。 解决方法: 检查应用的targetSdkVersion是否小于23的方法:
/** * 获取应用targetSdkVersion * @param context 上下文 * @return 应用targetSdkVersion */ private static int getTargetSdkVersion(Context context) { try { PackageInfo info = context.getPackageManager().getPackageInfo( context.getPackageName(), 0); return info.applicationInfo.targetSdkVersion; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return 0; }使用以下方法检查是否有权限:
/** * 检查是否有权限 * @param context 上下文 * @param permission 申请的权限 * @return true=有权限 false=无权限 */ public static boolean checkHavePermissions(Context context, String permission) { if (Build.VERSION.SDK_INT >= 23) { if (getTargetSdkVersion(context) >= 23) { return ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED; } else { return PermissionChecker.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED; } } // 如果Android版本低于6.0,则默认有权限 return true; }通过自定义的checkHavePermissions()方法,即可解决checkSelfPermission()返回失效的问题,用户授予了权限时返回true,未授予时返回false。
