Android6.0运行时权限封装(避免用户选择不再提示后无法获取权限的问题)
来源:互联网 发布:金十数据是真的吗 编辑:程序博客网 时间:2024/06/05 04:18
转载请标明出处:http://blog.csdn.net/donkor_/article/details/77326989
前言:
Android 6.0 为了保护用户隐私,将一些权限的申请放在了应用运行的时候去申请, 比如以往的开发中,开发人员只需要将需要的权限在清单文件中配置即可,安装后用户可以在设置中的应用信息中看到:XX应用以获取**权限。用户点击可以选择给应用相应的权限。此前的应用权限用户可以选择允许、提醒和拒绝。在安装的时候用户是已经知道应用需要的权限的。但是这样存在一个问题,就是用户在安装的时候,应用需要的权限十分的多(有些开发者为了省事,会请求一些不必要的权限或者请求全部的权限),这个时候用户在安装应用的时候也许并没有发现某些侵犯自己隐私的权限请求,安装之后才发现自己的隐私数据被窃取。其实Android6.0 动态权限一方面是为了广大用户考虑,另一方面其实是Google为了避免一些不必要的官司。
目前针对Android6.0权限适配的逻辑代码,网上已经很多了。这里针对用户拒绝了权限请求并且选择了“□ 不在提示”时该如何处理。
权限流程:
▲在Api 23中, 权限需要动态获取, 核心权限必须满足. 标准流程:
▲但这里有个问题,那就是在系统授权弹窗环节,提醒框会有个不再提示的复选框,如果用户点击不再提示,并拒绝授权,那么再下次授权的时候,系统授权弹窗的提示框就不会在提示,所以我们很有必要需要自定义权限弹窗提示框,那么流程图就变成如下了。
权限类型:
▲ 在图中,我们可以看到整个权限里,可以分为系统权限和特殊权限授权。系统权限中,又分为normal和dangerous类型。
normal:这个权限类型并不直接威胁到用户的隐私,可以直接在manifest清单里注册,系统会帮我们默认授权的。
dangerous:这个可以直接给app访问用户一些敏感的数据,不仅需要在manifest清单里注册,同时在使用的时候,需要向系统请求授权。
值得注意一点,这里有特殊权限授权的区别,分别是SYSTEM_ALERT_WINDOW 和 WRITE_SETTINGS,虽然这两个权限也是属于dangerous权限类型,但是这两个授权请求方式和其他dangerous权限是不一样的,需要特殊处理 。
案例介绍:
根据官方介绍,当发起首次权限请求时不会提供“不再提示”这一选择,但如果用户之前拒绝过权限请求并且再次发起权限请求时,权限请求对话框才会提供“不在提示”这一选项供用户选择,当用户选择了“不再提示”之后就不会再弹出权限请求对话框。各大手机厂商的定制系统对该逻辑进行了一定的改动,例如小米MIUI系统当用户首次选择拒绝权限时之后就会“不在提示”了,华为EMUI系统在首次弹出权限请求时就会提供“不在提示”的选项,三星部分系统始终不提供“不在提示”的选项。
当用户选择了“不在提示”时,开发者需要引导用户去设置页手动授权!
但是android 6.0系统并未提供对“不在提示”这一选项的监听,那么开发者该如何判断用户是否选择了“不在提示”这一选项呢?
答案是通过shouldShowRequestPermissionRationale()这一方法。
这一方法的作用是判断是否需要向用户解释为何需要请求该权限。
当首次发起权限请求时该方法返回false。
当第二次权限请求时该方法返回true。
当发起第二次权限请求并且当用户选择了“不再提示”这一选项时该方法返回false。 通过这个逻辑我们可以反推出:当进行第二次权限请求被拒绝并且shouldShowRequestPermissionRationale()返回false时,那么该用户一定是选择了“不再提示”这一选项。
▲以RECORD_AUDIO,CAMERA,READ_EXTERNAL_STORAGE为例。全部权限请求成功,直接看妹砸。下面看图 ヾ(◍°∇°◍)ノ゙ 没图我会乱说
▲当部分权限受限,且用户未选中“不再提示”,弹出对话框说明,并进行请求权限
▲当部分权限受限,且用户选中“不再提示”,弹出对话框说明,引导用户到设置页手动授权
▲主要代码BaseActivity.java
package com.donkor.demo.permission;import android.app.Activity;import android.app.AlertDialog;import android.content.Context;import android.content.DialogInterface;import android.content.Intent;import android.content.pm.PackageManager;import android.net.Uri;import android.os.Bundle;import android.provider.Settings;import android.support.annotation.NonNull;import android.support.v4.app.ActivityCompat;import android.support.v4.content.ContextCompat;import android.support.v7.app.AppCompatActivity;public class BaseActivity extends AppCompatActivity { private final int mRequestCode = 1024; private RequestPermissionCallBack mRequestPermissionCallBack; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } /** * 权限请求结果回调 * * @param requestCode * @param permissions * @param grantResults */ @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); boolean hasAllGranted = true; StringBuilder permissionName = new StringBuilder(); for (String s : permissions) { permissionName = permissionName.append(s + "\r\n"); } switch (requestCode) { case mRequestCode: { for (int i = 0; i < grantResults.length; ++i) { if (grantResults[i] == PackageManager.PERMISSION_DENIED) { hasAllGranted = false; //在用户已经拒绝授权的情况下,如果shouldShowRequestPermissionRationale返回false则 // 可以推断出用户选择了“不在提示”选项,在这种情况下需要引导用户至设置页手动授权 if (!ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[i])) { new AlertDialog.Builder(BaseActivity.this).setTitle("PermissionTest")//设置对话框标题 .setMessage("【用户选择了不再提示按钮,或者系统默认不在提示(如MIUI)。" + "引导用户到应用设置页去手动授权,注意提示用户具体需要哪些权限】" + "获取相关权限失败:" + permissionName + "将导致部分功能无法正常使用,需要到设置页面手动授权")//设置显示的内容 .setPositiveButton("去授权", new DialogInterface.OnClickListener() {//添加确定按钮 @Override public void onClick(DialogInterface dialog, int which) {//确定按钮的响应事件 //TODO Auto-generated method stub Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", getApplicationContext().getPackageName(), null); intent.setData(uri); startActivity(intent); dialog.dismiss(); } }).setNegativeButton("取消", new DialogInterface.OnClickListener() {//添加返回按钮 @Override public void onClick(DialogInterface dialog, int which) {//响应事件 // TODO Auto-generated method stub dialog.dismiss(); } }).setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { mRequestPermissionCallBack.denied(); } }).show();//在按键响应事件中显示此对话框 } else { //用户拒绝权限请求,但未选中“不再提示”选项 mRequestPermissionCallBack.denied(); } break; } } if (hasAllGranted) { mRequestPermissionCallBack.granted(); } } } } /** * 发起权限请求 * * @param context * @param permissions * @param callback */ public void requestPermissions(final Context context, final String[] permissions, RequestPermissionCallBack callback) { this.mRequestPermissionCallBack = callback; StringBuilder permissionNames = new StringBuilder(); for (String s : permissions) { permissionNames = permissionNames.append(s + "\r\n"); } //如果所有权限都已授权,则直接返回授权成功,只要有一项未授权,则发起权限请求 boolean isAllGranted = true; for (String permission : permissions) { if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_DENIED) { isAllGranted = false; if (ActivityCompat.shouldShowRequestPermissionRationale((Activity) context, permission)) { new AlertDialog.Builder(BaseActivity.this).setTitle("PermissionTest")//设置对话框标题 .setMessage("【用户曾经拒绝过你的请求,所以这次发起请求时解释一下】" + "您好,需要如下权限:" + permissionNames + " 请允许,否则将影响部分功能的正常使用。")//设置显示的内容 .setPositiveButton("确定", new DialogInterface.OnClickListener() {//添加确定按钮 @Override public void onClick(DialogInterface dialog, int which) {//确定按钮的响应事件 //TODO Auto-generated method stub ActivityCompat.requestPermissions(((Activity) context), permissions, mRequestCode); } }).show();//在按键响应事件中显示此对话框 } else { ActivityCompat.requestPermissions(((Activity) context), permissions, mRequestCode); } break; } } if (isAllGranted) { mRequestPermissionCallBack.granted(); return; } } /** * 权限请求结果回调接口 */ interface RequestPermissionCallBack { /** * 同意授权 */ void granted(); /** * 取消授权 */ void denied(); }}
▲SplashActivity.java中Button请求权限主要代码
requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE}, new RequestPermissionCallBack() { @Override public void granted() { Toast.makeText(SplashActivity.this, "权限获取成功,执行下一步操作", Toast.LENGTH_SHORT).show(); Intent intent = new Intent(SplashActivity.this, MainActivity.class); startActivity(intent); SplashActivity.this.finish(); } @Override public void denied() { Toast.makeText(SplashActivity.this, "部分权限获取失败,正常功能受到影响,2秒钟之后自动退出", Toast.LENGTH_LONG).show(); //2秒钟之后自动退出 mHandler.postDelayed(new Runnable() { @Override public void run() { SplashActivity.this.finish(); } }, 2000); } });
资料与参考:
http://www.cnblogs.com/cr330326/p/5181283.html
http://blog.csdn.net/caroline_wendy/article/details/50587230
妹砸照片及源码 CSDN下载地址 : http://download.csdn.net/download/donkor_/9938151
妹砸照片及源码Gayhub下载地址: https://github.com/ChenYXin/PermissionDemo
- Android6.0运行时权限封装(避免用户选择不再提示后无法获取权限的问题)
- Android6.0动态申请权限那些坑--以及避免用户选择不再提示后无法获取权限的问题
- Android6.0运行时权限的封装
- 关于Android6.0以上动态获取运行时权限、及无法正常运行百度地图API的问题
- android6.0运行时权限完美封装
- android6.0运行时权限完美封装
- Android6.0M运行时权限封装
- Android6.0运行时权限的理解和封装
- Android6.0处理不再询问的权限问题
- Android6.0运行时权限解析,RxPermissions的使用,自己封装一套权限框架
- Android6.0运行时权限解析,RxPermissions的使用,自己封装一套权限框架
- 打电话Demo及Android6.0的运行时权限问题
- 打电话Demo及Android6.0的运行时权限问题
- Android6.0以上权限获取权限的问题
- android6.0以上获取权限简单封装
- Android6.0运行时权限处理-超简单封装
- 52.自己动手封装Android6.0运行时权限相关代码
- Android6.0运行时权限(二)之封装
- Map按照key排序(升序,降序)---String
- LR 的上传文件与下载文件
- 接口测试工具抉择
- Thinkphp实现文件上传与删除
- android studio 文件编码格式乱码
- Android6.0运行时权限封装(避免用户选择不再提示后无法获取权限的问题)
- jfinal config包的主要类
- 配置参数
- QTP给combobox控件赋值
- 《统计学习方法》k近邻 kd树的python实现
- 设计模式简介(1)
- DataExcel 设置表格编辑控件 CellGridView
- Linux 复制虚拟机
- spring定时任务的Cron表达式