Android开发之6.0运行时权限处理

Permission概述
权限分组

权限分为九组,让用户授予所有权限由一个单一的行动包括。例如,授权联系人包括视图的能力和编辑联系人。

默示许可权限:用户可能会执行一个动作表明明确的意图,如:

  • 拍照
  • 选择一个联系人
  • 开始一个电话或短信

在这些情况下,用户的行动清楚地表明了自己的意图,而不需要许可或授权对话框。

运行时权限

应用程序可以请求权限访问信息或使用设备在安装后的任何能力。当用户需要执行一个应用程序中的一个动作,如使用设备的摄像头,应用程序可以请求对应的权限。用户也可以允许或拒绝任何安装后从Android设置任何应用程序的权限。

请求模式

您的权限策略取决于你要求的权限类型的清晰性和重要性。这些模式提供不同权限用户的方式介绍。指示功能权限直接请求就可以了,有些权限需要附带说明具体用途,还有一些权限提供记住选择,避免再次授权,影响用户体验

Android的权限请求方式使请求取决于系统版本和系统版本的应用程序的目标:

  • 如果设备运行的是Android 6(API Level 23)或更高,和应用程序的targetSdkVersion是23或更高,应用程序要求的权限由用户在运行时。用户可以在任何时间撤销权限,所以应用程序需要检查是否有权限在每次运行时。

  • 如果设备运行的是Android 5.1(API Level 22)或更低,或应用程序的targetSdkVersion等于或低于22时,系统会要求用户授予权限,当用户安装的应用程序。如果你添加一个新的权限的应用程序的更新版本,系统会要求用户授予权限时,用户的应用程序更新。一旦用户安装该应用程序,他们可以撤销许可的唯一途径是通过卸载应用程序。

拒绝权限

每当一个权限被拒绝应向用户解释使用该权限的具体用途,保证功能要求的权限总是行为为目的,应用程序应该说是需要许可,允许它提供了一种方法。而权限被拒绝的途径以下两种:

  • 由用户拒绝权限请求
  • 权限是默默的否认没有警告因为一旦选择“不要再问”从以前的权限请求用户

Permission分组
adb查看权限分组

在概述里面提过一点权限分组,其实我们可以通过adb命令查看更为详尽的分组信息,cmd切换到adb目录下运行下面命令

adb shell pm list permissions -d -g
  • group:android.permission-group.PHONE_CALLS

    • permission:android.permission.READ_PHONE_STATE
    • permission:android.permission.CALL_PHONE
    • permission:android.permission.USE_SIP
    • permission:android.permission.PROCESS_OUTGOING_CALLS
  • group:android.permission-group.WALLPAPER

    //…………..略………….

  • group:android.permission-group.MICROPHONE

  • ungrouped:
    • permission:org.simalliance.openmobileapi.SMARTCARD
    • permission:com.android.permission.ENABLE_HWQRCODEDISPATCHER
    • permission:com.huawei.phoneservice.permission.SMART_FAQS_ACCESS
    • permission:com.huawei.camera.permission.QRCODE_SCAN
    • permission:com.huawei.phoneservice.permission.CENTER_SERVICE_ACCESS

权限还可以分为危险权限和正常权限,还有特殊权限、自定义权限。正常权限(PROTECTION_NORMAL)没有大的风险对用户的隐私或安全让应用程序的权限。例如,用户可以合理地想知道一个应用程序可以读取他们的联系信息,所以用户必须授予该权限的明确。相比之下,在允许一个应用程序,使设备没有很大的风险,因此,允许指定为正常

如果一个应用程序声明其表现,它需要一个正常的权限,系统自动授予应用程序的权限在安装的时候。该系统不提示用户给予正常的权限,用户不能撤销这些权限。

正常权限列表



Permission申请
Manifest添加权限申请声明
<manifestxmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.snazzyapp">

<uses-permissionandroid:name="android.permission.SEND_SMS"/>


<application...>
...
</application>

</manifest>
Permission官方实践

官网上提供了Permission相关的两个zip文件,如果有兴趣可以去下载来阅读参考,如果你没用vpn也可以通过以下链接下载:

RuntimePermissionsBasic项目源码大概过了一遍,核心部分用到了ActivityCompat、ContextCompat、OnRequestPermissionsResultCallback。源码中以Camera为例,请求打开照相机,先要检测是否拥有Camera权限,返回对应的code值,在PackageManager里面有注解PermissionResult

 /** @hide */
    @IntDef({PERMISSION_GRANTED, PERMISSION_DENIED})
    @Retention(RetentionPolicy.SOURCE)
    public @interface PermissionResult {}

    /**
     * Permission check result: this is returned by {@link #checkPermission}
     * if the permission has been granted to the given package.
     * 允许了请求的权限
     */
    public static final int PERMISSION_GRANTED = 0;

    /**
     * Permission check result: this is returned by {@link #checkPermission}
     * if the permission has not been granted to the given package.
     * 拒绝了请求的权限
     */
    public static final int PERMISSION_DENIED = -1;

    /**
     * Signature check result: this is returned by {@link #checkSignatures}
     * if all signatures on the two packages match.
     * 签名匹配
     */
    public static final int SIGNATURE_MATCH = 0;

   //...........略................

请求权限先判断是否已经获得了该权限如果没有,需要阐述获取权限理由,官方demo用的Snackbar弹出提示,如果ok确认了获取权限

 private void showCameraPreview() {
        // BEGIN_INCLUDE(startCamera)
        // Check if the Camera permission has been granted
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
                == PackageManager.PERMISSION_GRANTED) {
            // Permission is already available, start camera preview
            Snackbar.make(mLayout,
                    "Camera permission is available. Starting preview.",
                    Snackbar.LENGTH_SHORT).show();
            startCamera();
        } else {
            // Permission is missing and must be requested.
            requestCameraPermission();
        }
        // END_INCLUDE(startCamera)
    }

ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)检查权限方法源自于父类ContextCompat,我们也可以直接调用它。如果某些权限必须获取,那么我们需要附加阐述理由,Snackbar弹出让用户选择确认了重新获取选项,如果非必须权限,则Snackbar提示information即可

 private void requestCameraPermission() {
        // Permission has not been granted and must be requested.
        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.
            // Display a SnackBar with a button to request the missing permission.
            Snackbar.make(mLayout, "Camera access is required to display the camera preview.",
                    Snackbar.LENGTH_INDEFINITE).setAction("OK", new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    // Request the permission
                    ActivityCompat.requestPermissions(MainActivity.this,
                            new String[]{Manifest.permission.CAMERA},
                            PERMISSION_REQUEST_CAMERA);
                }
            }).show();

        } else {
            Snackbar.make(mLayout,
                    "Permission is not available. Requesting camera permission.",
                    Snackbar.LENGTH_SHORT).show();
            // Request the permission. The result will be received in onRequestPermissionResult().
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},
                    PERMISSION_REQUEST_CAMERA);
        }
    }

透过请求权限方法我们可以发现,Activity需要实现interface OnRequestPermissionsResultCallback,回调函数onRequestPermissionsResult处理回调结果

 public static void requestPermissions(final @NonNull Activity activity,
            final @NonNull String[] permissions, final int requestCode) {
        if (Build.VERSION.SDK_INT >= 23) {
            ActivityCompatApi23.requestPermissions(activity, permissions, requestCode);
        } else if (activity instanceof OnRequestPermissionsResultCallback) {
            Handler handler = new Handler(Looper.getMainLooper());
            handler.post(new Runnable() {
                @Override
                public void run() {
                    final int[] grantResults = new int[permissions.length];

                    PackageManager packageManager = activity.getPackageManager();
                    String packageName = activity.getPackageName();

                    final int permissionCount = permissions.length;
                    for (int i = 0; i < permissionCount; i++) {
                        grantResults[i] = packageManager.checkPermission(
                                permissions[i], packageName);
                    }

                    ((OnRequestPermissionsResultCallback) activity).onRequestPermissionsResult(
                            requestCode, permissions, grantResults);
                }
            });
        }
    }

Activity实现接口 处理回调代码如下:

  @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions,
            int[] grantResults) {
        // BEGIN_INCLUDE(onRequestPermissionsResult)
        if (requestCode == PERMISSION_REQUEST_CAMERA) {
            // Request for camera permission.
            if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Permission has been granted. Start camera preview Activity.
                Snackbar.make(mLayout, "Camera permission was granted. Starting preview.",
                        Snackbar.LENGTH_SHORT)
                        .show();
                startCamera();
            } else {
                // Permission request was denied.
                Snackbar.make(mLayout, "Camera permission request was denied.",
                        Snackbar.LENGTH_SHORT)
                        .show();
            }
        }
        // END_INCLUDE(onRequestPermissionsResult)
    }

RuntimePermissions项目是一个关于Camera和读写相关的权限申请的综合运用,对我们来说提取有用信息非常必要的,就一个PermissionUtil类用于检测请求权限结果参数


public abstract class PermissionUtil {

    /**
     * Check that all given permissions have been granted by verifying that each entry in the
     * given array is of the value {@link PackageManager#PERMISSION_GRANTED}.
     *
     * @see Activity#onRequestPermissionsResult(int, String[], int[])
     */
    public static boolean verifyPermissions(int[] grantResults) {
        // At least one result must be checked.
        if(grantResults.length < 1){
            return false;
        }

        // Verify that each required permission has been granted, otherwise return false.
        for (int result : grantResults) {
            if (result != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }

}

Permission开源库

关于Permission这一块呢在github上有几个比较有名气的开源库

洪洋的MPermissions采用的代理、注解方式实现,注解了三种方式:请求权限被允许、请求权限被拒绝、阐述请求权限理由,根据请求权限结果代理的方式执行对应回调,代码中具体调用方式:

public class MainActivity extends AppCompatActivity
{

    private Button mBtnSdcard;
    private static final int REQUECT_CODE_SDCARD = 2;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBtnSdcard = (Button) findViewById(R.id.id_btn_sdcard);
        mBtnSdcard.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                MPermissions.requestPermissions(MainActivity.this, REQUECT_CODE_SDCARD, Manifest.permission.WRITE_EXTERNAL_STORAGE);
            }
        });
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
    {
        MPermissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }


    @PermissionGrant(REQUECT_CODE_SDCARD)
    public void requestSdcardSuccess()
    {
        Toast.makeText(this, "GRANT ACCESS SDCARD!", Toast.LENGTH_SHORT).show();
    }

    @PermissionDenied(REQUECT_CODE_SDCARD)
    public void requestSdcardFailed()
    {
        Toast.makeText(this, "DENY ACCESS SDCARD!", Toast.LENGTH_SHORT).show();
    }
}

先不说好坏,我也说不出什么,只能接着再看,找到最适合自己的就好。PermissionsDispatcher库过了一遍,也采用了注解的方式,该库应该是参考了官方源码,PermissionUtil相比较更为完善。不过对于我来说还不是我喜欢得方式,继续往下找Dexter,该库把请求权限结果封装成对象,并提供了定制Listener通过Builder方式构建,Application初始化,相对来说使用也挺方便,下面是调用方式:

{
PermissionListener dialogPermissionListener =
    DialogOnDeniedPermissionListener.Builder
        .withContext(context)
        .withTitle("Camera permission")
        .withMessage("Camera permission is needed to take pictures of your cat")
        .withButtonText(android.R.string.ok)
        .withIcon(R.mipmap.my_icon)
        .build();
Dexter.checkPermission(dialogPermissionListener, Manifest.permission.CAMERA);

PermissionListener snackbarPermissionListener =
    SnackbarOnDeniedPermissionListener.Builder
        .with(rootView, "Camera access is needed to take pictures of your dog")
        .withOpenSettingsButton("Settings")
        .build();
Dexter.checkPermission(snackbarPermissionListener, Manifest.permission.CAMERA);

//.............略...............
}

EasyPermissions库根据官方demo,定制Callback回调,同时也有用到注解,在请求权限里面获取传入接口直接判断满足条件调用callback回调函数

public static void onRequestPermissionsResult(int requestCode, String[] permissions,
                                                  int[] grantResults, Object object) {

        checkCallingObjectSuitability(object);
        PermissionCallbacks callbacks = (PermissionCallbacks) object;

        // Make a collection of granted and denied permissions from the request.
        ArrayList<String> granted = new ArrayList<>();
        ArrayList<String> denied = new ArrayList<>();
        for (int i = 0; i < permissions.length; i++) {
            String perm = permissions[i];
            if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                granted.add(perm);
            } else {
                denied.add(perm);
            }
        }

        // Report granted permissions, if any.
        if (!granted.isEmpty()) {
            // Notify callbacks
            callbacks.onPermissionsGranted(requestCode, granted);
        }

        // Report denied permissions, if any.
        if (!denied.isEmpty()) {
            callbacks.onPermissionsDenied(requestCode, denied);
        }

        // If 100% successful, call annotated methods
        if (!granted.isEmpty() && denied.isEmpty()) {
            runAnnotatedMethods(object, requestCode);
        }
    }

对于我个人编码习惯而言,该库最适合我,在BaseActivity实现回调接口PermissionCallbacks ,并提供protect 方法配置权限列表,我们具体代码调用(Activity、Fragment)需要就配置一下就ok了,在BaseActivity里面我们还可以预处理结果,在某种情况下我们可以忽略这些方法的实现。


装逼独白

以前,我只需要随便找到一个可用库连接,直接使用就可以了,然而并不关心怎么实现,而今,我觉得我们非常有必要了解代码的具体实现,还可以学到很多知识,扩展视野,学习大神的编码方式。也许你会说好困难啊,看不懂!!!其实不然,不懂得google baidu查资料总会明白的,官网也有的是资料,如果你又要说英文好难好难,你可以用有道翻译谷歌翻译啊,更何况还有百度翻译整个网页,一句话:借口就是借口,只要你想学,总会有办法,不要让借口为自己的未来买单。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值