今天在测试APP的时候发现部分手机APP定位权限使用不了。看了一下机型都是Android6.0以上版本的Android手机。
之前就听说Android6.0以上版本权限管理更严格了,没想到今天踩坑了。6.0版本之前的权限管理都是一些手机系统自己写的权限管理,比如说小米手机系统,魅族...这些都自己带有自己权限管理。
这样子容易出现的问题是:
1.如果某个权限被禁用了。应用开发中根本没提供方法来判断该权限是否被禁用。我们就需要自己的方法去判断。
比如说录音权限被禁用了:http://blog.csdn.net/omrapollo/article/details/51150280,是从录音功能下手做判断。而不是从权限列表获取权限状态。这是真的坑。
2.我们知道权限被禁用了,还需要判断一下是哪个系统好提醒用户按照操作去开启这个权限。
也就是说开发过程中需要把主流的机型测试一遍。然后如果有权限管理的话把权限流程过一遍。加个判别系统的方法,然后在把流程做成一个一个加上去。简直可怕。Google最终解决方案,给系统弄一个权限管理。
太棒了,权限管理由Android系统本身来做才是对开发者最友好的解决方案。

废话不多说,接下来就讲讲6.0版本权限问题的终极解决方案吧。

Android在 6.0以上版本需要进行动态的权限配置。

一、SDK新增方法

1.在ActivityCompat增加的方法

ActivityCompat.requestPermissions(context, needPermissions, REQUEST_CODE_REQUEST_PERMISSION);

这个方法用来动态配置权限。如果没有所需的权限。将会弹窗提醒用户配置权限。
三个参数分别为:当前上下文,需要申请的权限列表列表,请求码。请求码用于后面回调返回请求结果用。

ActivityCompat.checkSelfPermission(context, needPermission);

这个方法用来监测某个权限是否被配置。
两个参数分别为:当前上下文,需要监测的权限信息。
这个方法的返回值有两个0或-1 分别是已授权,和未授权

在PackManager这个类中有与之相匹配的常量

 /*** Permission check result: this is returned by {@link #checkPermission}* if the permission has been granted to the given package.*/public static final int PERMISSION_GRANTED = 0;/*** Permission check result: this is returned by {@link #checkPermission}* if the permission has not been granted to the given package.*/public static final int PERMISSION_DENIED = -1;

2.Activity中增加回调方法

@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[]grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);}

这个方法会在用户配置完权限后回调
三个回调参数分别是:请求码,请求的权限,权限当前状态

二、写个demo试验一下


package cn.xiaolongonly.myapplication;import android.Manifest;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;public class MainActivity extends AppCompatActivity {private String[] requestPermissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_FINE_LOCATION};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);int write = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);int read = ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);int coarse = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION);int fine = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);Log.d("permissionState:", "write:"+write + "_" + "read:"+read + "_" + "coarse:"+coarse + "_" + "fine:"+fine);ActivityCompat.requestPermissions(this, requestPermissions, 10086);}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);Log.d("RequestCode", requestCode + "");for (int i = 0; i < permissions.length; i++) {Log.d("permissionInfo", "permissionName:" + permissions[i] + "permission State:" + grantResults[i]);}int write = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);int read = ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);int coarse = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION);int fine = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);Log.d("permissionState:", "write:"+write + "_" + "read:"+read + "_" + "coarse:"+coarse + "_" + "fine:"+fine);}
}

打印出的Log如下

允许的情况

D/CreatePermissionState:: write:-1_read:-1_coarse:-1_fine:-1
D/RequestCode: 10086
D/permissionInfo: permissionName:android.permission.WRITE_EXTERNAL_STORAGEpermission State:0
D/permissionInfo: permissionName:android.permission.READ_EXTERNAL_STORAGEpermission State:0
D/permissionInfo: permissionName:android.permission.ACCESS_COARSE_LOCATIONpermission State:-1
D/permissionInfo: permissionName:android.permission.ACCESS_FINE_LOCATIONpermission State:-1
D/FinishPermissionState:: write:0_read:0_coarse:-1_fine:-1

拒绝后

D/CreatePermissionState:: write:-1_read:-1_coarse:-1_fine:-1
D/RequestCode: 10086
D/permissionInfo: permissionName:android.permission.WRITE_EXTERNAL_STORAGEpermission State:-1
D/permissionInfo: permissionName:android.permission.READ_EXTERNAL_STORAGEpermission State:-1
D/permissionInfo: permissionName:android.permission.ACCESS_COARSE_LOCATIONpermission State:-1
D/permissionInfo: permissionName:android.permission.ACCESS_FINE_LOCATIONpermission State:-1
D/FinishPermissionState:: write:-1_read:-1_coarse:-1_fine:-1

界面:

出现的问题

发现只出现一个授权文件读写的提醒框。
最后发现我在清单文件Manifest中只注册了读写权限。没有注册网络定位和精准定位权限。加上这两个。
这次启动的时候就出现了两个提示框了。(授权文件读写和定位)
可以看出,文件读写是归一个权限控制来管理的,两个定位权限也是归于一个权限控制来管理的。
清单权限注册

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

注意事项:

  1. 需要动态配置的权限一定要在清单文件中做注册,不然默认都是不做配置的。之前踩过的一个坑是只注册了COARSE_LOCATION没有注册FINE_LOCATION,发现FINE_LOCATION一直是未授权,但是COARSE_LOCATION授权成功了。

  2. 当用户拒绝权限授权之后,在次权限申请的时候是会出现不在询问的提示框的。勾选之后只能选择拒绝权限。拒绝之后以后就不会再做提醒了,
    权限会自动被拒绝,requestPermissions方法是有走的,系统自动拒绝了这个权限。所以在onRequestPermissionResult()中一样能看到不提醒但requestPermissions做了申请的权限。

三、最后再对代码进行简单的封装

package cn.xiaolongonly.myapplication;import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.provider.Settings;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.widget.Toast;public class PermissionUtil {public static final int REQUEST_CODE_REQUEST_PERMISSION = 1027;private static final String PACKAGE_URL_SCHEME = "package:";private static final int REQUEST_CODE_REQUEST_SETTING = 1028;public interface PermissionListener {void allGranted();                                          //申请的权限已全部被允许;}public static class PermissionTool {public String[] lackPermission;/*** 需要进行检测的权限数组*/public String[] mainNeedPermissions = {       //SD卡权限,定位权限Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_FINE_LOCATION,};/*** 需要进行检测的权限数组*/public String[] qrCodeNeedPermissions = {       //获取相机权限Manifest.permission.CAMERA};/*** 需要进行检测的权限数组*/public String[] requestNeedPermissions = {       //获取手机设备号用Manifest.permission.READ_PHONE_STATE};private PermissionListener mListener;public PermissionTool(PermissionListener listener) {mListener = listener;}public void checkAndRequestPermission(Activity activity, String... needPermissions) {boolean isAllGranted = true;for (String needPermission : needPermissions) {if (ActivityCompat.checkSelfPermission(activity, needPermission)!= PackageManager.PERMISSION_GRANTED) {isAllGranted = false;}Log.d("state", ActivityCompat.checkSelfPermission(activity, needPermission) + "");}if (isAllGranted) {mListener.allGranted();} else {ActivityCompat.requestPermissions(activity, needPermissions, REQUEST_CODE_REQUEST_PERMISSION);}}public void onRequestPermissionResult(final Activity activity,int requestCode, String[] permissions, int[] grantResults) {if (requestCode == REQUEST_CODE_REQUEST_PERMISSION) {boolean allGrant = true;for (int i = 0; i < grantResults.length; i++) {if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {allGrant = false;}Log.d("PermissionTool", "onRequestPermissionResult(): " + grantResults[i] + "permission" + permissions[i]);}if (allGrant) {mListener.allGranted();} else {Toast.makeText(activity, "部分所需权限未开启,将影响功能正常使用,请开启权限!", Toast.LENGTH_LONG).show();startAppSettings(activity);}}}public void startAppSettings(Activity activity) {Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);intent.setData(Uri.parse(PACKAGE_URL_SCHEME + activity.getPackageName()));activity.startActivityForResult(intent, REQUEST_CODE_REQUEST_SETTING);}}}

这里将权限管理的方法和回调都抽取到一个类中了。直接调用方法和回调就可以了。

值得注意的是:在自己写的APP有一些页面没有这些授权是无法使用的。但是用户又禁止了权限。而用户可能不知道要去哪里开启这个权限,就可以通过打开当前应用的设置页面来引导用户开启。startAppSettings(Activity activity)

使用:


package cn.xiaolongonly.myapplication;import android.Manifest;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;public class MainActivity extends AppCompatActivity {private String[] requestPermissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_FINE_LOCATION};private PermissionUtil.PermissionTool permissionTool;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);if (Build.VERSION.SDK_INT >= 23) { //针对6.0以后的版本加权限判断permissionTool = new PermissionUtil.PermissionTool(new PermissionUtil.PermissionListener() {@Overridepublic void allGranted() {initView();}});permissionTool.checkAndRequestPermission(MainActivity.this, requestPermissions);} else {initView();}}private void initView() {//在这里做界面初始化}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);permissionTool.onRequestPermissionResult(this, requestCode, permissions, grantResults);}
}

总结对于Android6.0运行时权限暂时就知道怎么配置了,如果有不足的地方欢迎指出。
demo下载

新功能

这边顺便讲一下在manifest清单文件中多了

 <uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION"/>

这样的权限设置,这个跟应用动态授权没有什么关系。
在谷歌官方文档
是这么解释的

指明应用需要特定权限,但仅当应用在 API 级别 23 或更高版本的设备上运行时才需要。如果设备运行的是 API 级别 22 或更低版本,则应用没有指定的权限。
当您更新应用以包含需要其他权限的新功能时,此元素很有用。如果用户在运行 API 级别 22 或更低版本的设备更新应用,系统在安装时会提示用户授予在该更新中声明的所有新权限。如果某个新功能无关紧要,您可能想同时在这些设备上停用该功能,以便用户不需要授予额外权限即可更新应用。如果使用 <uses-permission-sdk-23> 元素而不使用 <uses-permission>,则仅当应用在支持运行时权限模式(用户在应用运行时向其授予权限)的平台上运行时才可请求权限。
如需了解有关权限的详细信息,请参阅简介的权限部分和单独的系统权限 API 指南。在 android.Manifest.permission 上可以找到基础平台定义的权限列表。

也就是说如果使用的是来注册的话这个权限就只能运行时动态赋予。 而版本低于SDK23的Android设备不支持动态设置权限。所以低于SDK23的应用是不能有这个权限的。

好消息好消息

谷歌Developers中国网站今天发布了。

英文四级的我感到非常happy。

毕竟看英文文档都要借助翻译一个单词一个单词查还是很辛苦的,现在文档都有官方的中文版本了。

手动撒花!!!!手动撒花!!!!手动撒花!!!!

Android6.0运行时权限解决方案相关推荐

  1. 这可能是最精简的Android6.0运行时权限处理,百行代码的工具类,支持Rationale,附:各种权限详细处理

    0x00:前言 对于Android6.0运行时权限的处理方式网上有很多,包括注解,RxJava等等.一直没有正面提到我关心的问题–如果我不在Activity或者Fragment里面,需要运行时权限该怎 ...

  2. 获取权限android sync,GitHub - AndSync/XPermissionUtils: 可能是最精简的Android6.0运行时权限处理方式,支持Rationale提示...

    # XPermissionUtils 可能是最精简的Android6.0运行时权限处理方式,支持Rationale提示,只有一个类,100行代码,所有弹窗等操作由用户自行处理,在Demo中也有提供代码 ...

  3. Android6.0运行时权限处理

    前言 在Android6.0版本以前,往往是应用程序需要什么权限直接在manifest.xml中直接声明,当你安装程序的时候,如果不想让该程序使用某种权限,唯一的办法只能是不装这个应用,但是我们生活中 ...

  4. Android6.0运行时权限(危险权限列表)

    从 Android 6.0(API 23)开始,用户开始在应用运行时向其授予权限,而不是在应用安装时授予.这种权限机制可以让用户更好的管理应用的权限,保障用户隐私. Android将权限分为普通权限和 ...

  5. Android8.0运行时权限策略变化和适配方案

    版权声明:转载必须注明本文转自严振杰的博客:http://blog.yanzhenjie.com Android8.0也就是Android O即将要发布了,有很多新特性,目前我们可以通过Android ...

  6. Android6.0执行时权限解析,RxPermissions的使用,自己封装一套权限框架

    Android6.0执行时权限解析,RxPermissions的使用.自己封装一套权限框架 在Android6.0中,新添加了一个执行时的权限,我相信非常多人都已经知道了.预计也知道怎么用了,这篇博客 ...

  7. Android 8.0 运行时权限策略变化和适配方案

    Android8.0也就是Android O即将要发布了,有很多新特性,目前我们可以通过AndroidStudio3.0 Canary版本下载Android O最新的系统映像的Developer Pr ...

  8. Android 8.0学习(18)--- Android8.0运行时权限策略变化和适配方案

    Android8.0运行时权限策略变化和适配方案    在 Android O 之前,如果应用在运行时请求权限并且被授予该权限,系统会错误地将属于同一权限组并且在清单中注册的其他权限也一起授予应用.对 ...

  9. Android 6.0 运行时权限处理完全解析

    一.概述 随着Android 6.0发布以及普及,我们开发者所要应对的主要就是新版本SDK带来的一些变化,首先关注的就是权限机制的变化.对于6.0的几个主要的变化,查看查看官网的这篇文章http:// ...

最新文章

  1. 不用再找换脸教程了,飞桨PaddleGAN给你一键式体验
  2. 【NLP】四万字全面详解 | 深度学习中的注意力机制(二)
  3. fou循环 php 剩余次数_php for 循环语句使用方法详细说明
  4. 2016年第七届蓝桥杯 - 省赛 - C/C++大学A组 - F. 寒假作业
  5. 测试上传图片晰度+测试多数量图片上传
  6. Linux高级编程--01.vi命令
  7. 不止代码:迷宫问题(bfs)
  8. 2010年3月再谈前端工程师的笔试题
  9. C++ 多态的两种形式
  10. SQL极限函数limit()详解分页必备
  11. vasp软件全名是什么_vasp软件主要功能
  12. 小米手机adb命令解锁BL_小米10解锁BL刷机卸载自带APP等
  13. 【统计学】基本Stata使用手册:合集
  14. vue滑杆_Vue无限滑杆组件
  15. 光猫H2-3交换机K2P|K2Padavan无线路由器单臂路由上网
  16. android用服务建立悬浮窗,Android悬浮窗用法总结
  17. 辽宁特色名吃:哈鱼饼子与辣丝驴肉
  18. 关于今年执行的减肥计划
  19. 准确率Accuracy与损失函数Loss的关系
  20. 数据库设计和功能需求分析------后台设计概述

热门文章

  1. EditPlus_3.4及注册码
  2. 文献阅读(41) Retrain-Less/DSIP
  3. 【Leetcode_easy】748. Shortest Completing Word
  4. codevs【1152】细胞分裂
  5. MNN 实现NV12转BGR格式
  6. 数据结构--停车场管理
  7. Android使用动画实现微信扫描线效果
  8. 建立带头结点的单链表
  9. 易度文档管理系统-功能试用体验
  10. XGBoost 与 Boosted Tree