6.0运行时权限的总结与实践

运行时权限.png
运行时权限.png

为什么需要6.0运行时权限

  • 更友好

6.0以前的安装时权限,会在应用安装时列出所有需要的权限,当列出一些危险权限时,用户不知每个权限的具体用途,可能因为这些权限警告而放弃安装应用。对于一些非装不可的应用,用户则不得不被迫接受所有权限,很容易安装了一些流氓APP,体验不佳。
6.0以后的运行时权限,可以在调用相关功能之前判断权限授权状态,并自定义提示弹框告知用户权限用途,使用户清楚了解之后,再授权使用。

5.0和6.0的安装页面.png
5.0和6.0的安装页面.png
  • 更稳定

6.0系统的手机对于每个应用,都有个权限设置页面,可以手动开关权限,如果用户在设置页面误关了某个权限,若没在程序运行时做判断,则会导致相关功能的调用失败,引起崩溃等。

5.0和6.0的权限设置页面.png
5.0和6.0的权限设置页面.png

如何实现运行时权限

  • 设置targetSdkVersion

只有targetSdkVersion>=23,且安装在6.0以上的手机时,运行时权限机制才能正常运作

手机系统 targetSdkVersion<23 targetSdkVersion>=23
5.0+ 安装时权限 安装时权限
6.0+ 安装时权限 运行时权限
  • 代码实现

在需要使用某项权限时,通过V4包的checkSelfPermission判断权限是否授权,通过requestPermissions申请某项权限

// 需要执行某项需要权限的操作时
if (ContextCompat.checkSelfPermission(this, "某项权限") == PackageManager.PERMISSION_GRANTED) {
   // 执行操作
} else {
   // 请求权限
   ActivityCompat.requestPermissions(this, new String[]{"某项权限"}, requestCode);
}

在Activity的onRequestPermissionsResult回调方法中,处理权限授权结果

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
   if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
       // 授权成功,执行操作
   } else {
       // 权限被拒绝
       if (ActivityCompat.shouldShowRequestPermissionRationale(this, "权限名称")) {
           // shouldShowRequestPermissionRationale=true: 表示权限被拒绝,且没有勾选"never ask again"
           // 正常的权限被拒绝流程,可以继续申请权限,重复以上流程
       } else {
           // shouldShowRequestPermissionRationale=false: 在权限被拒绝,且勾选"never ask again"的情况下,返回false
           // 继续申请权限的时候,不会再弹出默认的系统弹框,需要自定义提示弹框,并引导用户去权限设置页面,手动开启权限
       }
   }
}

LsPermission工具类的使用

  • 主要实现逻辑参考PermissionGen,封装了权限判断,请求,结果处理等通用逻辑,目前只支持context=AppCompatActivity,如果在Fragment中使用时可以调用getActivity()获取上层AppCompatActivity。
demo演示.gif
demo演示.gif
private void normal() {
   final String[] permissions =
       new String[] {Manifest.permission.CALL_PHONE, Manifest.permission.ACCESS_FINE_LOCATION};

   PermissionUtil.request(this, permissions, new OnPermissionAdapter() {
       /**
        * @desc 申请的权限全被授权
        */
       @Override
       public void onGrant() {
           showToast("权限被同意");
           callPhone();
       }

       /**
        * @desc 权限被拒绝
        */
       @Override
       public void onDeny(List<String> permissions) {
           showToast("用户拒绝授权" + permissions.toString());
       }

       /**
        * @desc 权限被拒绝,且勾选"never ask again"
        */
       @Override
       public void onNeverAsk(List<String> permissions) {
           showToast("用户拒绝授权, 并勾选 never ask again " + permissions.toString());
           PermissionUtil.showNeverAskDialog(MainActivity.this, "这个权限很重要");
       }
       
       /**
        * @desc 无论权限授权成功还是失败,都会回调
        */
       @Override
       public void always(List<String> grantPermissions, List<String> denyPermissions,
           List<String> foreverDenyPermissions) {
           showToast("授权: " + grantPermissions.toString() + "\n 拒绝: " + denyPermissions.toString()
               + "\n never ask: " + foreverDenyPermissions.toString());
       }
   });
}

/**
* @desc 将权限回调转发给PermissionUtil处理
* @author listen
* @date 2017/2/24 13:47
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
   PermissionUtil.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
   super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
  • PermissionUtil内部通过SparseArray保存了requestCode和权限回调的键值对,避免了连续点击时重复回调情况,对于不同的权限申请,推荐使用不同的requestCode,避免回调覆盖的问题。
private static SparseArray<OnPermissionListener> mPermissionRequestList = new SparseArray<>();

public static void request(final Context context, int requestCode, String[] permissions,
                          OnPermissionListener listener) {
   /** 存在未授权的权限 */
   synchronized (mPermissionRequestList) {
       if (null != mPermissionRequestList.get(requestCode)) {
           /** 当前权限请求已经存在,不重复添加 */
           log("the same permission is requesting");
       } else {
           /** 将当前权限请求加入队列 */
           /** 如果一个页面中存在分别触发A, B多个权限的情况, 则最好将不同权限申请对应不同的requestCode, 存入SparseArray分别处理 */
           mPermissionRequestList.put(requestCode, listener);

           /** 执行权限申请 */
           ActivityCompat.requestPermissions();
       }
   }
}

在权限回调的时候从SparseArray移除Listener

public static void onRequestPermissionsResult(Context context, int requestCode, String[] permissions,
                                                  int[] grantResults) {
   final OnPermissionListener listener;
   synchronized (mPermissionRequestList) {
       listener = mPermissionRequestList.get(requestCode);
       mPermissionRequestList.remove(requestCode);
   }

   if (null != listener) {
       /** 执行回调 */
       listener.onGrant();
       listener.onDeny();
       listener.onNeverAsk();
       listener.always();
   } else {
       log("request is not exists");
   }
}

代码地址:LsPermission

除了基本的权限申请逻辑的封装以外,还写了类似微信,支付宝,百度地图等在启动页的权限申请Demo,算是PermissionUtil的简单运用。

参考:
Android 6.0 运行时权限处理完全解析
Android 6.0 运行时权限管理最佳实践
聊一聊Android 6.0的运行时权限

官方:
在运行时请求权限
权限最佳做法

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

推荐阅读更多精彩内容