6.0 权限问题

以下是需要单独申请的权限,共分为9组,每组只要有一个权限申请成功了,就默认整组权限都可以使用了。

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

其他权限是普通权限,在Manifiest.xml中申请即可
危险权限则需要在代码中动态申请

运行时权限处理

Android6.0系统默认为targetSdkVersion小于23的应用默认授予了所申请的所有权限,所以如果你以前的APP设置的targetSdkVersion低于23,在运行时也不会崩溃,但这也只是一个临时的救急策略,用户还是可以在设置中取消授予的权限,当取消后权限对应的功能依旧无法使用(可能也会崩溃)。

为什么要动态申请权限
如果不动态申请权限,手机也会弹出权限申请窗口,但是一旦用户拒绝之后,程序会崩溃
故需要动态申请权限

如何处理安卓6.0权限

权限状态

有的手机权限分为三种状态(有的手机只有允许和禁止,如小米手机)

  • 允许
    当处于这个状态时,表示权限已经拿到
  • 询问
    可弹出系统的权限弹窗
  • 禁止
    此时无法弹出系统的权限弹窗,只能手动去设置界面修改

对于只有允许和禁止状态的手机,第一次请求权限会弹出权限弹窗,但是一旦拒绝之后就不会弹出了

关键方法解析

ContextCompat.checkSelfPermission(Context context,String permission)

检查传入的permission,有木有拿到

 boolean   shouldShowRequestPermissionRationale

该方法在ActivityCompat和FragmentCompat中都有

如果用户是第一次去请求此权限时,此方法返回false(所以应该先申请权限,在调用此方法)
如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true
如果用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了 Don't ask again 选项,此方法将返回 false
如果设备规范禁止应用具有该权限,此方法也会返回 false(如小米手机)

一般情况下,我们会在该方法返回true时,展示一个UI,来告诉用户我们为什么需要此权限

requestPermissions

调用该方法,则会去申请权限
如果是权限是询问状态,则会弹出一个系统的权限弹窗
如果是权限是禁止状态,则直接返回权限被拒绝

onRequestPermissionsResult

申请权限时,权限的结果会从这里获取到
如果权限时禁止状态,则结果为拒绝
如果是询问状态,则结果取决于用户时允许还是拒绝

Activity的权限处理

点击按钮时

    if (ContextCompat.checkSelfPermission(this,   Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
         //申请权限
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100);
    }else {
        //权限是允许的,执行自己的逻辑
    }

写一个回调

public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
        case 100:
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                //用户同意授权,执行自己的逻辑
            } else {
                //用户拒绝授权,在这里区分一下两种拒绝的情况(shouldShowRequestPermissionRationale)      
            }
            break;
    }
}

Fragment的权限处理

参考网上的说法,没测试

在Fragment中申请权限,不要使用ActivityCompat.requestPermissions, 直接使用Fragment的requestPermissions方法,否则会回调到Activity的onRequestPermissionsResult

如果在Fragment中嵌套Fragment,在子Fragment中使用requestPermissions方法,onRequestPermissionsResult不会回调回来,建议使用getParentFragment().requestPermissions方法,这个方法会回调到父Fragment中的onRequestPermissionsResult,加入以下代码可以把回调透传到子Fragment

 public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    List<Fragment> fragments = getChildFragmentManager().getFragments();
    if (fragments != null) {
        for (Fragment fragment : fragments) {
            if (fragment != null) {
                fragment.onRequestPermissionsResult(requestCode,permissions,grantResults);
            }
        }
    }
}

关于权限的封装

推荐使用RxPermissions
https://github.com/tbruyelle/RxPermissions

/**
 * 权限工具类
 */
public class PermissionUtil {

  /**
   * 申请单个权限
   */
  public static Observable<Permission> requestPermission(FragmentActivity activity,
  String permissionName) {
    return requestPermission(activity, permissionName, "", "");
  }

  /**
   * 申请单个权限
   * refuseHint 权限被拒绝后的提示
   * settingHint 权限被拒绝,并勾选不再询问的提示
   */
  public static Observable<Permission> requestPermission(FragmentActivity activity,
      String permissionName, String refuseHint, String settingHint) {
    if (activity == null || TextUtils.isEmpty(permissionName)) {
      return Observable.just(new Permission("", false));
    }

    return new RxPermissions(activity).requestEach(permissionName).doOnNext(permission -> {
      if (permission.granted) {
        // 权限允许
      } else if (permission.shouldShowRequestPermissionRationale) {
        // 权限拒绝,但未勾选不再提示
        showRefuseHint(activity, refuseHint);
      } else {
        // 权限拒绝,并勾选不再提示,需要跳转至设置界面
        showSettingDialog(activity, settingHint);
      }
    });
  }


  /**
   * 申请多个权限,有一个拒绝则返回false
   */
  public static Observable<Boolean> requestPermissions(FragmentActivity activity,
  String[] permissions) {
    return requestPermissions(activity, permissions, "", "");
  }

  /**
   * 申请多个权限,有一个拒绝则返回false
   */
  public static Observable<Boolean> requestPermissions(FragmentActivity activity,
  String[] permissions, String refuseHint, String settingHint) {
    if (activity == null || permissions.length == 0) {
      return Observable.just(Boolean.FALSE);
    }
    return new RxPermissions(activity).request(permissions).doOnNext(aBoolean -> {
      if (!aBoolean) {
        // 权限被拒
        List<String> denyPermissions = new ArrayList<>();
        // 筛选出被拒绝的权限
        for (int i = 0; i < permissions.length; i++) {
          if (!hasPermission(activity, permissions[i])) {
            denyPermissions.add(permissions[i]);
          }
        }

        boolean shouldShowRequestPermissionRationale = false;
        for (String permision : denyPermissions) {
          // 有一个未勾选不再提示,则置为true
          if (shouldShowRationale(activity, permision)) {
            shouldShowRequestPermissionRationale = true;
          }
        }
        if (shouldShowRequestPermissionRationale) {
          showRefuseHint(activity, refuseHint);
        } else {
          showSettingDialog(activity, settingHint);
        }
      }
    });
  }


  /**
   * 拒绝权限,但未勾选不再提醒的提示
   */
  private static void showRefuseHint(FragmentActivity activity, String hint) {
    if (TextUtils.isEmpty(hint)) {
      return;
    }
    ToastUtil.startShort(activity, hint);
  }

  /**
   * 拒绝权限,并勾选不再提醒的弹窗
   */
  private static void showSettingDialog(FragmentActivity activity, String hint) {
    if (TextUtils.isEmpty(hint)) {
      return;
    }

    AlertDialog.Builder builder = new AlertDialog.Builder(activity)
        .setMessage(hint)
        .setPositiveButton("去设置", new DialogInterface.OnClickListener() {
          @Override
          public void onClick(DialogInterface dialog, int which) {
            startAppSettingActivity(activity);
          }
        })
        .setNegativeButton("取消", new DialogInterface.OnClickListener() {
          @Override
          public void onClick(DialogInterface dialog, int which) {
            dialog.dismiss();
          }
        });
    builder.show();
  }

  /**
   * 跳转至设置界面
   * 此方法并不能适配所有手机
   */
  public static void startAppSettingActivity(FragmentActivity activity) {
    try {
      Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
      intent.setData(Uri.fromParts("package", activity.getPackageName(), null));
      activity.startActivity(intent);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }


  /**
   * 判断是否用某个权限
   */
  public static boolean hasPermission(Context context, String permission) {
    if (context == null || TextUtils.isEmpty(permission)) {
      return false;
    }
    return Build.VERSION.SDK_INT < Build.VERSION_CODES.M
    || ContextCompat.checkSelfPermission(context,
        permission) == PackageManager.PERMISSION_GRANTED;
  }

  /**
   * 返回true,表示权限被拒绝,但是未勾选不再提示
   */
  public static boolean shouldShowRationale(FragmentActivity activity, String permission) {
    if (activity == null || TextUtils.isEmpty(permission)) {
      return false;
    }
    return ActivityCompat.shouldShowRequestPermissionRationale(activity, permission);
  }
}

本章完

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,671评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,442评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,524评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,623评论 1 275
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,642评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,584评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,953评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,621评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,865评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,608评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,698评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,378评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,958评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,940评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,173评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,419评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,425评论 2 342

推荐阅读更多精彩内容

  • Android 6.0权限 权限理解: Android在每台设备(系统)上增加了一些安全管理:通过一些列权限来限制...
    yaoTongxue阅读 375评论 0 3
  • 有几种情况。 1 如果项目的目标sdk,设置为23以下,即6.0以下。 a 不用做权限处理。 用户的手机不管是6....
    阿星_阅读 162评论 0 0
  • 关于6.0以上权限:新的权限机制更好的保护了用户的隐私,Google将权限分为两类,一类是Normal Permi...
    学习不断阅读 527评论 0 0
  • 又是平常的一天,御欣走路的方式极特别,喜欢学猫走,先是把腿踢直出去,然后落脚时,放在后一条腿前面,呈一直线。她认为...
    千熙龙陵阅读 171评论 0 0
  • 星星,也许黯淡 但仍会崛起 或许,在月晕的周围 那颗最亮的 就是以前 我们指着的 那一颗黯淡的星 它,换了身份...
    紫气东来1998阅读 187评论 0 2