转自:http://www.cnblogs.com/dubo-/p/6018262.html

Android 6.0 - 动态权限管理的解决方案

 

转载请标注

Android 6.0版本(Api 23)推出了很多新的特性, 大幅提升了用户体验, 同时也为程序员带来新的负担. 动态权限管理就是这样, 一方面让用户更加容易的控制自己的隐私, 一方面需要重新适配应用权限. 时代总是不断发展, 程序总是以人为本, 让我们为应用添加动态权限管理吧! 这里提供了一个非常不错的解决方案, 提供源码, 项目可以直接使用.

Permissions

Android系统包含默认的授权提示框, 但是我们仍需要设置自己的页面. 原因是系统提供的授权框, 会有不再提示的选项. 如果用户选择, 则无法触发授权提示. 使用自定义的提示页面, 可以给予用户手动修改授权的指导.

本文示例的GitHub下载地址

在Api 23中, 权限需要动态获取, 核心权限必须满足. 标准流程:

流程图

如果用户点击, 不再提示, 则系统授权弹窗将不会弹出. 流程变为:

流程图

流程就这些, 让我们看看代码吧.


1. 权限

在AndroidManifest中, 添加两个权限, 录音修改音量.

    <!--危险权限-->
<uses-permission android:name="android.permission.RECORD_AUDIO"/> <!--一般权限-->
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>

危险权限必须要授权, 一般权限不需要.

检测权限类

/**
* 检查权限的工具类
* <p/>
* Created by wangchenlong on 16/1/26.
*/
public class PermissionsChecker {
private final Context mContext; public PermissionsChecker(Context context) {
mContext = context.getApplicationContext();
} // 判断权限集合
public boolean lacksPermissions(String... permissions) {
for (String permission : permissions) {
if (lacksPermission(permission)) {
return true;
}
}
return false;
} // 判断是否缺少权限
private boolean lacksPermission(String permission) {
return ContextCompat.checkSelfPermission(mContext, permission) ==
PackageManager.PERMISSION_DENIED;
}
}

2. 首页

假设首页需要使用权限, 在页面显示前, 即onResume时, 检测权限,
如果缺少, 则进入权限获取页面; 接收返回值, 拒绝权限时, 直接关闭.

public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_CODE = 0; // 请求码

    // 所需的全部权限
static final String[] PERMISSIONS = new String[]{
Manifest.permission.RECORD_AUDIO,
Manifest.permission.MODIFY_AUDIO_SETTINGS
}; @Bind(R.id.main_t_toolbar) Toolbar mTToolbar; private PermissionsChecker mPermissionsChecker; // 权限检测器 @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this); setSupportActionBar(mTToolbar); mPermissionsChecker = new PermissionsChecker(this);
} @Override protected void onResume() {
super.onResume(); // 缺少权限时, 进入权限配置页面
if (mPermissionsChecker.lacksPermissions(PERMISSIONS)) {
startPermissionsActivity();
}
} private void startPermissionsActivity() {
PermissionsActivity.startActivityForResult(this, REQUEST_CODE, PERMISSIONS);
} @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// 拒绝时, 关闭页面, 缺少主要权限, 无法运行
if (requestCode == REQUEST_CODE && resultCode == PermissionsActivity.PERMISSIONS_DENIED) {
finish();
}
}
}

核心权限必须满足, 如摄像应用, 摄像头权限就是必须的, 如果用户不予授权, 则直接关闭.


3. 授权页

授权页, 首先使用系统默认的授权页, 当用户拒绝时, 指导用户手动设置, 当用户再次操作失败后, 返回继续提示. 用户手动退出授权页时, 给使用页发送授权失败的通知.

/**
* 权限获取页面
* <p/>
* Created by wangchenlong on 16/1/26.
*/
public class PermissionsActivity extends AppCompatActivity { public static final int PERMISSIONS_GRANTED = 0; // 权限授权
public static final int PERMISSIONS_DENIED = 1; // 权限拒绝 private static final int PERMISSION_REQUEST_CODE = 0; // 系统权限管理页面的参数
private static final String EXTRA_PERMISSIONS =
"me.chunyu.clwang.permission.extra_permission"; // 权限参数
private static final String PACKAGE_URL_SCHEME = "package:"; // 方案 private PermissionsChecker mChecker; // 权限检测器
private boolean isRequireCheck; // 是否需要系统权限检测 // 启动当前权限页面的公开接口
public static void startActivityForResult(Activity activity, int requestCode, String... permissions) {
Intent intent = new Intent(activity, PermissionsActivity.class);
intent.putExtra(EXTRA_PERMISSIONS, permissions);
ActivityCompat.startActivityForResult(activity, intent, requestCode, null);
} @Override protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getIntent() == null || !getIntent().hasExtra(EXTRA_PERMISSIONS)) {
throw new RuntimeException("PermissionsActivity需要使用静态startActivityForResult方法启动!");
}
setContentView(R.layout.activity_permissions); mChecker = new PermissionsChecker(this);
isRequireCheck = true;
} @Override protected void onResume() {
super.onResume();
if (isRequireCheck) {
String[] permissions = getPermissions();
if (mChecker.lacksPermissions(permissions)) {
requestPermissions(permissions); // 请求权限
} else {
allPermissionsGranted(); // 全部权限都已获取
}
} else {
isRequireCheck = true;
}
} // 返回传递的权限参数
private String[] getPermissions() {
return getIntent().getStringArrayExtra(EXTRA_PERMISSIONS);
} // 请求权限兼容低版本
private void requestPermissions(String... permissions) {
ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE);
} // 全部权限均已获取
private void allPermissionsGranted() {
setResult(PERMISSIONS_GRANTED);
finish();
} /**
* 用户权限处理,
* 如果全部获取, 则直接过.
* 如果权限缺失, 则提示Dialog.
*
* @param requestCode 请求码
* @param permissions 权限
* @param grantResults 结果
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == PERMISSION_REQUEST_CODE && hasAllPermissionsGranted(grantResults)) {
isRequireCheck = true;
allPermissionsGranted();
} else {
isRequireCheck = false;
showMissingPermissionDialog();
}
} // 含有全部的权限
private boolean hasAllPermissionsGranted(@NonNull int[] grantResults) {
for (int grantResult : grantResults) {
if (grantResult == PackageManager.PERMISSION_DENIED) {
return false;
}
}
return true;
} // 显示缺失权限提示
private void showMissingPermissionDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(PermissionsActivity.this);
builder.setTitle(R.string.help);
builder.setMessage(R.string.string_help_text); // 拒绝, 退出应用
builder.setNegativeButton(R.string.quit, new DialogInterface.OnClickListener() {
@Override public void onClick(DialogInterface dialog, int which) {
setResult(PERMISSIONS_DENIED);
finish();
}
}); builder.setPositiveButton(R.string.settings, new DialogInterface.OnClickListener() {
@Override public void onClick(DialogInterface dialog, int which) {
startAppSettings();
}
}); builder.show();
} // 启动应用的设置
private void startAppSettings() {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse(PACKAGE_URL_SCHEME + getPackageName()));
startActivity(intent);
}
}

注意isRequireCheck参数的使用, 防止和系统提示框重叠.
系统授权提示: ActivityCompat.requestPermissions, ActivityCompat兼容低版本.

效果

自定义授权

关键部分就这些了, 动态权限授权虽然给程序员带来了一些麻烦, 但是对用户还是很有必要的, 我们也应该欢迎, 毕竟每个程序员都是半个产品经理.

危险权限列表

Android 6.0 - 动态权限管理的解决方案(转)的更多相关文章

  1. Android 6.0 - 动态权限管理的解决方案

    Android 6.0版本(Api 23)推出了很多新的特性, 大幅提升了用户体验, 同时也为程序员带来新的负担. 动态权限管理就是这样, 一方面让用户更加容易的控制自己的隐私, 一方面需要重新适配应 ...

  2. android 6.0 动态权限

    Android 6.0 动态权限: 除了要在AndroidManifest.xml中申请外,还需使用时,请求用户允许授权. 以下是需要单独申请的权限,共分为9组,每组只要有一个权限申请成功了,就默认整 ...

  3. Android 6.0动态权限申请

    转载(Android 6.0 动态权限申请简单简洁优雅的处理方式): https://blog.csdn.net/lin_dianwei/article/details/79025324

  4. Android 6.0 动态权限申请注意事项

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/uana_777/article/details/54136255 Part One 权限区分 And ...

  5. Android 6.0动态权限(转)

    转自:http://blog.csdn.net/uana_777/article/details/54136255 Part One 权限区分 Android 6.0 为了保护用户隐私,将一些权限的申 ...

  6. android 6.0+ 动态权限 拒绝不再询问后跳转设置应用详情页面

    android 6.0+ 的权限 需要动态申请 这里的权限针对的是 敏感权限: SMS(短信) SEND_SMS RECEIVE_SMS READ_SMS RECEIVE_WAP_PUSH RECEI ...

  7. Android 6.0 动态权限申请

    1. 概述 Android 6.0 (API 23) 之前应用的权限在安装时全部授予,运行时应用不再需要询问用户.在 Android 6.0 或更高版本对权限进行了分类,对某些涉及到用户隐私的权限可在 ...

  8. Android 6.0之权限管理

    安卓6.0的权限体系分为非敏感权限和敏感权限,非敏感权限默认获取,可以手动关闭. 敏感权限必须由app在运行时动态申请.而存储读写空间权限是一个敏感权限,不是一个“很正常的必须权限”. 安卓并不是想要 ...

  9. Android 6.0动态权限申请教程

    转载请标明出处: http://www.cnblogs.com/why168888/p/6580396.html 本文出自:[Edwin博客园] PermissionManage 介绍 如果设备运行的 ...

随机推荐

  1. 18.synchronized

    同步的前提:  必须要有两个或者两个以上的线程  必须是多个线程使用同一个锁  必须保证同步中只能有一个线程在运行  好处:解决了多线程的安全问题  弊端:多个线程需要判断锁,较为消耗资源.抢锁的资源 ...

  2. Charles IOS https抓包

    步骤 1.下载charles: https://www.charlesproxy.com/download/ 只有一个30天试用版,每次打开只能30分钟,如果想时间长点,就找破解版或者买个licenc ...

  3. SSD的理解,为PyramidBox做准备

    目标检测主流方法有两大类 two-stage,以rcnn系列为主,采用建议框的方式对目标进行预测,过程首先要经过网络生成候选框,分类背景前景与进行第一次回归,之后再进行一次精细回归. 优点是准确率高, ...

  4. MySQL事务调优

    原创转载请注明出处:https://www.cnblogs.com/agilestyle/p/11429239.html 数据库事务 数据库事务是数据库系统执行过程中的一个逻辑处理单元,保证一个数据库 ...

  5. Quartz.Net 任务调度之简单任务(1)

    本文github链接 https://github.com/sunshuaize/cnBlogDemos/tree/master/Quartz.Net%20%E4%BB%BB%E5%8A%A1%E8% ...

  6. python学习--第二天 爬取王者荣耀英雄皮肤

    今天目的是爬取所有英雄皮肤 在爬取所有之前,先完成一张皮肤的爬取 打开anacond调出编译器Jupyter Notebook 打开王者荣耀官网 下拉找到位于网页右边的英雄/皮肤 点击[+更多] 进入 ...

  7. 基于jquery和bootstrap的下拉框左右选择功能

    实现如图选择的功能,可以用基于bootstrap的样式,结合jquery事件: <div class="row"> <div class="col-xs ...

  8. js的 算法 和 数据结构

    js的 算法 1.对一个对象数组按照对象某个属性进行排序  : https://www.cnblogs.com/webcabana/p/7460038.html 在做公交的项目中就碰到过这种算法问题, ...

  9. [CSP-S模拟测试]:简单的填数(贪心+模拟)

    题目描述 对于一个长度为$n$,且下标从$1$开始编号的序列$a$,我们定义它是「合法的」,当且仅当它满足以下条件:·$a_1=1$·对于$i\in [1,n),a_i\leqslant a_{i+1 ...

  10. python中的pow()函数解释

    转载自:https://blog.csdn.net/wuxiaobingandbob/article/details/47776209 Python中pow(),里面可以有两个或三个参数,它们的意义是 ...