欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

详解Android权限管理之Android 6.0运行时权限及解决办法

程序员文章站 2024-02-28 13:52:16
前言: 今天还是围绕着最近面试的一个热门话题android 6.0权限适配来总结学习,其实android 6.0权限适配我们公司是在今年5月份才开始做,算是比较晚的吧...

前言:

今天还是围绕着最近面试的一个热门话题android 6.0权限适配来总结学习,其实android 6.0权限适配我们公司是在今年5月份才开始做,算是比较晚的吧,不过现在android 6.0以上设备越来越多了,所以android 6.0 权限适配是必不可少的工作,这里主要介绍一下我们公司是如何做android 6.0权限适配的。

android 6.0以下非运行时权限:

根据上面博客我们很清楚的知道,android的权限其实就是为了程序之间更加的安全的访问,所以权限有等级之分,比如:normal 低风险权限 、dangerous  高风险权限等,虽然有这种安全意识,但是这些权限只会在安装的时候被询问一次,一旦安装之后,如果app申请了高风险权限的话,而且大部分用户在安装的时候很少去关注这些权限列表,再加上很多android市场都有静默安装的功能用户更加感知不到任何权限提示,就这样app就有可能会在后台做一些对用户带来伤害的事情。如下图所示:
详解Android权限管理之Android 6.0运行时权限及解决办法

android6.0运行时权限:

鉴于6.0之前的版本权限管理相对不那么安全,所以android 6.0 采用新的权限模型,只有在需要权限的时候,才告知用户是否授权,是在runtime时候授权,而不是在原来安装的时候 ,同时默认情况下每次在运行时打开页面时候,需要先检查是否有所需要的权限申请。这样的用户的自主性提高很多,比如用户可以给app赋予摄像的权限,也可以使用权限。
详解Android权限管理之Android 6.0运行时权限及解决办法 

android 6.0权限适配:

1.)不进行适配造成的现象

先看下app module的build.gradle配置

 compilesdkversion 24
 buildtoolsversion "24.0.2"
 defaultconfig {
  applicationid "com.whoislcj.rxpermissions"
  minsdkversion 15
  targetsdkversion 24
  versioncode 1
  versionname "1.0"
 }

由于android 6.0 以上的权限变成了运行时权限,也就是说在需要使用某个权限的时候必须动态去申请使用,直接访问直接导致app崩溃。详解Android权限管理之Android 6.0运行时权限及解决办法

2.)早期的解决办法 

其实判断是否是需要运行时权限的标记就是targetsdkversion,当targetsdkversion<23的时候,仅在安装时赋予权限,使用时将不被提醒,当targetsdkversion≥23的时候才会使用新的运行时权限规则。所有在最早遇见因权限未适配的导致的崩溃的时候,我们团队采用的解决办法是将targetsdkversion人为的降到小于23,这样就变成了还是默认使用权限,但是这种并不是google所推荐使用的。

 compilesdkversion 24
 buildtoolsversion "24.0.2"
 defaultconfig {
  applicationid "com.whoislcj.rxpermissions"
  minsdkversion 15
  targetsdkversion 22
  versioncode 1
  versionname "1.0"
 }

3.)判断是否拥有该权限的使用权限

检查是否拥有使用权

 public boolean isgranted(string permission) {
  return !ismarshmallow() || isgranted_(permission);
 }

判断是否是android 6.0以上

 private boolean ismarshmallow() {
  return build.version.sdk_int >= build.version_codes.m;
 }

是否申请了该使用权限

 private boolean isgranted_(string permission) {
  int checkselfpermission = activitycompat.checkselfpermission(this, permission);
  return checkselfpermission == packagemanager.permission_granted;
 }

contextcompat.checkselfpermission,主要用于检测某个权限是否已经被授予,方法返回值为packagemanager.permission_denied或者packagemanager.permission_granted。当返回denied就需要进行申请授权了。

4.)申请使用权限

private void requestpermission(string permission, int requestcode) {
  if (!isgranted(permission)) {
   if (activitycompat.shouldshowrequestpermissionrationale(this, permission)) {

   } else {
    activitycompat.requestpermissions(this, new string[]{permission}, requestcode);
   }
  } else {
   //直接执行相应操作了
  }
 }

shouldshowrequestpermissionrationale主要用于给用户一个申请权限的解释,该方法只有在用户在上一次已经拒绝过你的这个权限申请。也就是说,用户已经拒绝一次了,你又弹个授权框,你需要给用户一个解释,为什么要授权,则使用该方法。requestcode这个需要在处理的回调的时候 一一对应的。

5.)处理授权回调

@override
 public void onrequestpermissionsresult(int requestcode, string[] permissions, int[] grantresults) {
  if (requestcode == camera) {
   if (grantresults[0] == packagemanager.permission_granted) {
    string jpgpath = getcachedir() + "test.jpg";
    takephotobypath(jpgpath, 2);
   } else {
    // permission denied
    toast.maketext(mainactivity.this, "您没有授权该权限,请在设置中打开授权", toast.length_short).show();
   }
   return;
  }
  super.onrequestpermissionsresult(requestcode, permissions, grantresults);
 }

6.)完整的activity示例
 

public class mainactivity extends appcompatactivity {
 private static final int camera = 2;

 @override
 protected void oncreate(bundle savedinstancestate) {
  super.oncreate(savedinstancestate);
  setcontentview(r.layout.activity_main);
  findviewbyid(r.id.request_permission).setonclicklistener(new view.onclicklistener() {
   @override
   public void onclick(view v) {
    requestpermission(manifest.permission.camera, camera);
   }
  });
 }

 /**
  * 拍照,返回拍照文件的绝对路径
  */
 private string takephotobypath(string filepath, int requestcode) {
  file file = new file(filepath);
  startactivityforresult(gettakephotointent(file), requestcode);
  return file.getpath();
 }

 private intent gettakephotointent(file file) {
  if (file.exists()) {
   file.delete();
  }

  try {
   file.createnewfile();
  } catch (ioexception e) {
   e.printstacktrace();
  }

  uri uri = uri.fromfile(file);
  intent intent = new intent(mediastore.action_image_capture);
  intent.putextra(mediastore.extra_output, uri);
  return intent;
 }

 public boolean isgranted(string permission) {
  return !ismarshmallow() || isgranted_(permission);
 }

 private boolean isgranted_(string permission) {
  int checkselfpermission = activitycompat.checkselfpermission(this, permission);
  return checkselfpermission == packagemanager.permission_granted;
 }

 private boolean ismarshmallow() {
  return build.version.sdk_int >= build.version_codes.m;
 }

 //shouldshowrequestpermissionrationale主要用于给用户一个申请权限的解释,该方法只有在用户在上一次已经拒绝过你的这个权限申请。也就是说,用户已经拒绝一次了,你又弹个授权框,你需要给用户一个解释,为什么要授权,则使用该方法。
 private void requestpermission(string permission, int requestcode) {
  if (!isgranted(permission)) {
   if (activitycompat.shouldshowrequestpermissionrationale(this, permission)) {

   } else {
    activitycompat.requestpermissions(this, new string[]{permission}, requestcode);
   }
  } else {
   //直接执行相应操作了
  }
 }

 @override
 public void onrequestpermissionsresult(int requestcode, string[] permissions, int[] grantresults) {
  if (requestcode == camera) {
   if (grantresults[0] == packagemanager.permission_granted) {
    string jpgpath = getcachedir() + "test.jpg";
    takephotobypath(jpgpath, 2);
   } else {
    // permission denied
    toast.maketext(mainactivity.this, "您没有授权该权限,请在设置中打开授权", toast.length_short).show();
   }
   return;
  }
  super.onrequestpermissionsresult(requestcode, permissions, grantresults);
 }

}

总结:

本篇总结学习了android 6.0的运行时权限及如何适配的问题,但是这个并不是我们公司目前最终的解决办法,从上面可以看出实现起来还是蛮麻烦的,申请权限和处理回调在不同的地方代码可读性相对较差,我们最终的解决方案是采用rxjava+rxpermission的方式解决,下一篇将介绍一下如何使用rxpermission解决android 6.0 权限适配问题。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。