解读Android官方开发指导 - 运行时权限

bytebuilder
• 阅读 4162

解读Android官方开发指导 - 运行时权限

系统权限简介

Android出于系统稳定性以及用户隐私方面的考虑,将应用程序访问权限限制在各自的沙盒内。程序可以随意访问所在沙盒内部的资源或者信息,访问沙盒外部的则必须明确的申请相关访问权限。应用程序所需要的权限需要在AndroidManifest.xml文件中申明。如:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"    
        package="com.douyoumi.permission">

    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  

    <application ...>    
    ...  
    </application>

</manifest>

(本文出处:http://www.jianshu.com/p/0beb...

系统权限根据敏感程度分为普通权限危险权限两类。两类权限都需要在AndroidManifest.xml文件中申明。在Android 5.1 (API level 22) 及其以下版本上,系统在APP安装时要求用户授权所有权限,否则APP不能安装;而在Android 6.0及其以上版本上,系统在APP安装时授权所有普通权限,危险权限需要在使用时动态让用户授权。这使得Android的权限管理更加灵活,用户可以根据需要在设置应用中对应用的各个危险权限授予不同的权限。Android系统的权限管理不知道被多少人吐槽过,这一改进无疑是加分项。

解读Android官方开发指导 - 运行时权限

权限申请官方开发指导

Android 6.0上使用在AndroidManifest.xml中已经申明的危险权限时需要用户授权。针对权限请求相关操作系统提供了三个API。下面结合Android官方的开发指导对这几个API做下说明。

  1. checkSelfPermission() 检查是否已经具有了相关权限。任何时候APP都要在执行需要危险权限的操作前去检查是否具有相关权限,即使刚刚执行过这项操作,因为用户很有可能去设置应用中关闭了相关权限。

  2. shouldShowRequestPermissionRationale() 判断是否需要向用户解释,为什么需要这些权限。有时候用户会不理解应用程序为什么需要这些权限。如,相机应用申请摄像头使用权限用户容易理解,但是相机应用申请地理位置使用权限可能会让用户产生疑惑,因为用户很有能不知道相机需要保存每张照片的拍摄地点。这时候我们就需要做适当的解释说明了。这个方法只有在APP请求过某一权限且用户禁止APP使用该权限的时候返回true。在用户授权了权限和禁止权限时勾选了“Don't ask again”选项的情况下都会返回false。Android官方开发指导还提到一点,为避免给用户带来糟糕的用户体验,这里的解释说明应该是异步的,不要阻塞用户的操作。时下很多适配了6.0的APP在这点上处理的都不尽如人意,有的根本没有解释说明,有的是弹出对话框,用户体验都不是很好。下文会给出了一个完美的解决方案。

  3. requestPermissions() 申请相关权限。调用这个方法后会弹出一个系统对话框来向用户申请权限,APP不能自定义这个对话框的内容,这也就增加了上面提到的解释说明的必要性。这里还有一点也需要交代一下。从上面危险权限列表中也可以看出,这些权限都是有分组的。如,READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE权限就是属于STORAGE组的。分门别类不仅仅是为了方便容易阅读,组内权限在申请上也是有关联的。

    • 在申请组内某个权限时,弹出的系统对话框会显示组名,而不是指明所申请的权限名。如,申请READ_EXTERNAL_STORAGE权限时,系统对话框提示请求“访问sd卡”权限,但不会说明是请求的sd卡读权限;

    • 申请权限时,如果组内有别的权限已经获得了用户授权,系统不再弹出询问对话框,而是自动授权该权限。如,在申请WRITE_EXTERNAL_STORAGE权限时用户已经授权了READ_EXTERNAL_STORAGE权限,系统则会自动授权WRITE_EXTERNAL_STORAGE权限,不再询问用户;

    • 即使有前一条规则存在,在使用每一条权限时都必须(不是应该)调用requestPermissions()方法来申请权限。如,在已经获取了READ_EXTERNAL_STORAGE权限的情况下,使用WRITE_EXTERNAL_STORAGE权限时依然需要调用requestPermissions()方法来申请,否则就会因为权限问题导致写sd卡失败。

注意:危险权限在AndroidManifest.xml文件中也必须申明,否则动态申请会失败。

下面代码是Android官方开发指导中权限申请大致框架。它使用了Android Support Library中的方法。虽然Android 6.0以后的framework中都有这些方法,但是对于开发者来说使用Android Support Library中的方法更简单,不用检查sdk版本可以兼容低版本。

// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {  
    // Should we show an explanation?  
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity, Manifest.permission.READ_EXTERNAL_STORAGE)) {    

        // Show an expanation to the user *asynchronously* -- don't blockthis thread waiting for the user's response!     
        // After the user sees the explanation, try again to request the permission. 

    } else {    
        // No explanation needed, we can request the permission. 
        ActivityCompat.requestPermissions(thisActivity, new String[] {Manifest.permission.READ_EXTERNAL_STORAGE}, MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);    
        // MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE is anapp-defined int constant.     
        // The callback method gets the result of the request.  
    }
}

requestPermissions()申请有结果后会回调onRequestPermissionsResult()方法,这种方式对于Android开发者一定不会陌生,因为与startActivityForResult()结果回调onActivityResult()方法类似。如下重载onRequestPermissionsResult()方法即可。

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {  
    switch (requestCode) {    
        case MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE: {      
            // If request is cancelled, the result arrays are empty.      
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {        
                // permission was granted, yay! Do the contacts-related task you need to do.      
            } else {        
                // permission denied, boo! Disable the functionality that depends on this permission. 
            }      
            return;    
        }    
    
    // other 'case' lines to check for other permissions this app might request  
    }
}

由于我们使用了AndroidSupport Library库记得在build.gradle文件中添加依赖。

dependencies {    
    ...
    compile 'com.android.support:appcompat-v7:23.2.1'
}

动态权限申请封装类EasyPermissionsEx

以上就是Android官方开发指导关于动态权限申请的全部内容了。系统的API已经很简洁了,但是用在项目中时依然会出现很多重复代码,按惯例是要封装一下。在github上搜索"Android permissions"会有很多开源库。Google在github上也开源了一个关于动态权限的封装库easypermissions。看了几个库后,个人觉得easypermissions写的最好,但是也有不足的地方。如,解释为什么需要权限的地方这个库也弹出一个对话框阻塞了用户操作,不符合开发规范、还有用户禁止权限时勾选了“Don't ask again”后依然会弹出对话框请求权限,用户体验不好。所以我在easypermissions库的基础封装了一个EasyPermissionsEx库。说是一个库,其实只有一个300行左右的类,你可以直接拷贝到你的项目中使用。

前文也提到过在解释为什么需要权限或者在用户永久禁止权限后引导用户去设置应用开启权限时,使用对话框会给用户带来不好的体验。EasyPermissionsEx采用的解决方案是使用snackbar来提醒用户。整体效果如下。snackbar是一种轻量级的用户交互,它不会阻塞用户当前的操作,是从底部弹出一个bar来提示用户,类似toast,但是snackbar又有一个button允许用户操作。关于snackbar更多信息可以跳转到官方文档了解。

解读Android官方开发指导 - 运行时权限

Snackbar在Android Design Support Library库中记得在build.gradle文件中添加依赖。

dependencies {    
    ...
    compile 'com.android.support:design:23.4.0'
}

EasyPermissionsEx权限请求逻辑如下。关于EasyPermissionsEx更多详情可以去github查看wiki,也可以直接看代码,因为它总共也就300行代码。

解读Android官方开发指导 - 运行时权限

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
梦
4年前
微信小程序new Date()转换时间异常问题
微信小程序苹果手机页面上显示时间异常,安卓机正常问题image(https://imghelloworld.osscnbeijing.aliyuncs.com/imgs/b691e1230e2f15efbd81fe11ef734d4f.png)错误代码vardate'2021030617:00:00'vardateT
Stella981 Stella981
3年前
Android笔记
  首先是权限,因为WIFI也是可以用于定位的,只是精度没那么高而已,所以6.0之后的权限系统对WIFI的权限进行了限制,除了需要日常的WIFI操作权限之外,还需要定位权限,否则会获取不到WIFI的信息。<!完全的网络访问权限<usespermissionandroid:name"android.pe
Stella981 Stella981
3年前
Android 设置系统闹铃和日历
一、创建定时器 设置权限<usespermissionandroid:name"com.android.alarm.permission.SET_ALARM"/案例https://developer.android.com/reference/android/provider/Alar
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
mysql用户
1\.学会能按着需求创建一个帐号2\.知道连接字符串是什么样3\.密码密码怎么恢复mysql用户权限介绍mysql用户管理 !(https://oscimg.oschina.net/oscnet/368d3c1e00a0a9515545c2962660a27a080.png)!(https://oscimg.oschin
Wesley13 Wesley13
3年前
Unity横屏
Android下发现Unity里面的Player设置,并不能完全有效,比如打开了自动旋转,启动的时候还是会横屏,修改XML添加以下代码<applicationandroid:icon"@drawable/ic\_launcher"                    android:label"@string/app\_name"
Stella981 Stella981
3年前
Android加载网络图片
AndroidManifest.xml文件中加入以下权限设置:    <usespermissionandroid:name"android.permission.INTERNET"/    <usespermissionandroid:name"android.permission.WRITE\_EXTERNAL\_STORAGE
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这