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);
  }
}

本章完

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

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

友情链接更多精彩内容