Android 6.0 Marshmallow运行时权限处理

在最新的Android6.0Marshmallow系统中,Google加入了在程序运行时检测权限的机制,下面这些权限是需要在运行时进行判断的:

  • 身体传感器(<uses-permission android:name="android.permission.BODY_SENSORS"/>)
  • 日历(<uses-permission android:name="android.permission.WRITE_CALENDAR"/>)
  • 摄像头(<uses-permission android:name="android.permission.CAMERA"/>)
  • 通讯录( <uses-permission android:name="android.permission.READ_CONTACTS"/>)
  • 地理位置(<uses-permission android:name="android.permission.LOCATION_HARDWARE"/>)
  • 麦克风(<uses-permission android:name="android.permission.RECORD_AUDIO"/>)
  • 电话(<uses-permission android:name="android.permission.CALL_PHONE"/>)
  • 短信(<uses-permission android:name="android.permission.SEND_SMS"/>)
  • 存储空间(<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>)
上面只是针对相应的内容列出了一个具体的权限,具体还有其他的权限。

如果设置项目的targetSdkVersion>=23,当程序运行在Android6.0以上的手机上时,默认是不授予权限的,可以在应用详细信息的权限选项中看到,如图:

 

如果targetSdkVersion设置在6.0以下的程序安装在了6.0上,则会默认打开这些权限,可以用此办法来作为应急的解决办法。

下面就来看看如何针对Android6.0的这一特性在运行时申请权限。

1、在Activity中请求权限

ContextCompat
	在ContextCompat类里新增了静态方法checkSelfPermission(@NonNull Context context,@NonNull Permission permission);来检查当前是否已经拥有了相应的权限。该方法会返回一个int值,对应PackageManager.PERMISSION_GRANTED或者PackageManager.PERMISSION_DENIED两个常量,通常可以用下面的写法:

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
                != PackageManager.PERMISSION_GRANTED) {
            // Camera permission has not been granted.

            requestCameraPermission();

        } else {

            // Camera permissions is already available, show the camera preview.
            Log.i(TAG,
                    "CAMERA permission has already been granted. Displaying camera preview.");
            showCameraPreview();
        }
上面的代码是Google RuntimePermissions Demo中的一段内容,代码很简单,如果没有权限就调用requestCameraPermission()方法去请求权限,如果已经有了权限就调用showCameraPreview()来调用相机。其中ActivityCompat类继承了ContextCompat类,因此也有了checkSelfPermission()方法。另外在ActivityCompat类中还有另外两个静态方法:requestPermissions(final @NonNull Activity activity,final @NonNull String[] permissions, final int requestCode);和shouldShowRequestPermissionRationale(@NonNull Activity activity,@NonNull String permission);下面来看一下requestCameraPermission()是如何实现的:
/**
     * Requests the Camera permission.
     * If the permission has been denied previously, a SnackBar will prompt the user to grant the
     * permission, otherwise it is requested directly.
     */
    private void requestCameraPermission() {
        Log.i(TAG, "CAMERA permission has NOT been granted. Requesting permission.");

        // BEGIN_INCLUDE(camera_permission_request)
        if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                Manifest.permission.CAMERA)) {
            // Provide an additional rationale to the user if the permission was not granted
            // and the user would benefit from additional context for the use of the permission.
            // For example if the user has previously denied the permission.
            Log.i(TAG,
                    "Displaying camera permission rationale to provide additional context.");
            Snackbar.make(mLayout, R.string.permission_camera_rationale,
                    Snackbar.LENGTH_INDEFINITE)
                    .setAction(R.string.ok, new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            ActivityCompat.requestPermissions(MainActivity.this,
                                    new String[]{Manifest.permission.CAMERA},
                                    REQUEST_CAMERA);
                        }
                    })
                    .show();
        } else {

            // Camera permission has not been granted yet. Request it directly.
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},
                    REQUEST_CAMERA);
        }
        // END_INCLUDE(camera_permission_request)
    }

在requestCameraPermission中又进行了一次判断,那这次判断又是为什么呢?其实这次判断是为了给用户一个更好的提示,当前Activity为第一次请求权限时,shouldShowRequestPermissionRationale()方法会返回false,这时候调用requestPermissions方法会默认显示系统的权限申请窗口,如下图:

	但是这个界面并没有任何提示,用户可能会选择拒绝或者允许,如果用户在这时选择了拒绝,当他再次执行同样的操作申请权限时,显然不能重复的显示这个界面而不给用户任何提示,我们知道这样的用户体验会很不好,因此就可以通过调用shouldShowRequestPermissionRationale()方法来判断是否用户之前拒绝过权限申请,我们可以在这个方法返回true的时候,给用户一点提示,说明程序为什么要申请这个权限,也就是上面代码中SnackBar.make...中的提示信息,下面是拒绝权限后再次点击的效果:

	从上面代码我们知道点击OK会再次进行权限申请,这样的逻辑无疑会提高程序的用户体验。
	调用requestPermissions方法后,结果会回调到Activity的方法:public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,@NonNull int[] grantResults);中,其中requestCode就是调用requestPermissions方法传入的requestCode。grantResults里包含了请求的结果。根据grantResults就可以判断用户是否授予了权限:
/**
     * Callback received when a permissions request has been completed.
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
            @NonNull int[] grantResults) {

        if (requestCode == REQUEST_CAMERA) {
            // BEGIN_INCLUDE(permission_result)
            // Received permission result for camera permission.
            Log.i(TAG, "Received response for Camera permission request.");

            // Check if the only required permission has been granted
            if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Camera permission has been granted, preview can be displayed
                Log.i(TAG, "CAMERA permission has now been granted. Showing preview.");
                Snackbar.make(mLayout, R.string.permision_available_camera,
                        Snackbar.LENGTH_SHORT).show();
            } else {
                Log.i(TAG, "CAMERA permission was NOT granted.");
                Snackbar.make(mLayout, R.string.permissions_not_granted,
                        Snackbar.LENGTH_SHORT).show();

            }
            // END_INCLUDE(permission_result)

        } else if (requestCode == REQUEST_CONTACTS) {
            Log.i(TAG, "Received response for contact permissions request.");

            // We have requested multiple permissions for contacts, so all of them need to be
            // checked.
            if (PermissionUtil.verifyPermissions(grantResults)) {
                // All required permissions have been granted, display contacts fragment.
                Snackbar.make(mLayout, R.string.permision_available_contacts,
                        Snackbar.LENGTH_SHORT)
                        .show();
            } else {
                Log.i(TAG, "Contacts permissions were NOT granted.");
                Snackbar.make(mLayout, R.string.permissions_not_granted,
                        Snackbar.LENGTH_SHORT)
                        .show();
            }

        } else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

除了使用ActivityCompat类中的静态方法外,Activity中也封装了requestPermissions(),checkSelfPermission(),shouldShowRequestPermissionRationale()这几个方法,用法类似。
2、在Fragment中请求权限
	在Fragment中请求权限时,不要调用Activity的requestPerminssions方法,因为结果会回调到Activity的onRequestPermissionsResult里,不利于在Fragment中进行处理,而是需要调用Fragment中相同的方法(参见:https://www.aswifter.com/2015/11/04/android-6-permission/ Fragment中也有requestPermissions和shouldShowRequestPermissionRationale这两个方法,checkSelfPermissions可以使用Activity中的)。
3.相关的开源项目(参见:https://www.aswifter.com/2015/11/04/android-6-permission/)
 
 
 
 
使用标注的方式,动态生成类处理运行时权限,目前还不支持嵌套Fragment
基于RxJava的运行时权限检测框架 简化运行时权限的处理,比较灵活
Google官方的例子






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值