Android 新安全机制之动态权限申请

文章来源:http://blog.csdn.net/zhuwentao2150/article/details/53449711

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_SETTINGS
 
 
  • 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
  • 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

Dangerous 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
 
 
  • 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
  • 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

可以发现,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) {
            // 无权限
        }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2、requestPermissions(Activity activity, String[] permissions, int requestCode): 
申请权限,会弹出一个系统对话框向用户申请权限,String[] permissions 传入一个或一组权限,int requestCode为请求码,用于在回调监听中标识权限申请的结果。

ActivityCompat.requestPermissions(context, new String[]{Manifest.permission.CAMERA}, STATE_CODE);
 
 
  • 1
  • 1

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;
        }
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

4、shouldShowRequestPermissionRationale(Activity activity, String permission): 
申请权限的解释,该方法只在大于等于6.0的版本起作用,在6.0前的版本只返回false,它的返回结果有以下几种情况

  • 当申请对应权限时被用户拒绝了一次,再次申请对应权限时该方法就会返回true
  • 如果用户在第二次时选择了“不再提醒”,则再次请求权限时该方法只会返回false
  • 用户在手机权限管理中设置为“禁止”,则该方法返回false
        if (ActivityCompat.shouldShowRequestPermissionRationale(context, Manifest.permission.CAMERA)) {
            // 用户再次拒绝时
        }
 
 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

三、常见问题:

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;
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

使用以下方法检查是否有权限:

    /**
     * 检查是否有权限
     * @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;
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

通过自定义的checkHavePermissions()方法,即可解决checkSelfPermission()返回失效的问题,用户授予了权限时返回true,未授予时返回false。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值