Android M Permission 学习笔记

Android应用权限简要介绍

一个Android应用默认状况下是不拥有任何权限的, 这便是说, 在默认状况下, 一个应用是没有权利去进行一些可能会形成很差影响的操做的. 这些很差的影响多是对其它应用,操做系统,或者是用户.
若是应用须要一些额外的能力,则它须要在AndroidManifest.xml中静态地声明相应的权限.
 
若是应用没有在manifest中声明权限, 却使用了相应的功能, 在调用到相应功能的时候, 将会抛出异常.
好比程序要发送一个请求,却忘记加Internet权限, 那么在发送这个请求的时候程序就会抛出异常,通常不会catch这个异常,因此程序直接就崩溃了:
Caused by: java.lang.SecurityException: Permission denied (missing INTERNET permission?)
 
 
Android 6.0 (API 23) 发布以前, 全部的权限都在安装应用的时候显示给用户,用户选择安装则表示所有接受这些权限, 以后没法撤销对这些权限的受权.
Android 6.0开始, 一部分比较危险的权限须要在程序运行时显式弹框,请求用户受权.
至于何时弹这个框,由应用程序本身决定.
对于其余权限,认为不是很危险,因此仍然保持原来的作法,在用户安装应用程序时就予以受权.
还须要注意的是,在设置中,对于应用的危险权限,用户能够选择性地进行受权或者关闭.
 

Permission的保护等级

permission的保护等级经过protectionLevel属性设置, 共有4种:  normal,dangerous,signature,signatureOrSystem.
 
签名相关的比较不经常使用, 剩下的两种是 normaldangerous.
Guides里面对这两种类型进行了讨论: 官网Guides:  https://developer.android.com/guide/topics/security/permissions.html#normal-dangerous
总结下来就是: 全部的权限仍然在manifest中静态声明, normal权限的在安装的时候自动受权, 而dangerous的权限须要应用明确地请求用户受权.
固然对于Android 6.0如下的手机,或者之前开发的旧应用来讲, dangerous权限也是安装时受权的, 具体看下一节的讨论.
 
Dangerous Permissions: 
 
 
想要查看全部dangerous的权限, 也能够用命令:
adb shell pm list permissions -g -d
  

手机版本和程序版本的不一样处理

这里引用一段Guildes里面的原文:
  • If the device is running Android 6.0 (API level 23) or higher, and the app's targetSdkVersion is 23 or higher, the app requests permissions from the user at run-time. The user can revoke the permissions at any time, so the app needs to check whether it has the permissions every time it runs. For more information about requesting permissions in your app, see the Working with System Permissions training guide.
  • If the device is running Android 5.1 (API level 22) or lower, or the app's targetSdkVersion is 22 or lower, the system asks the user to grant the permissions when the user installs the app. If you add a new permission to an updated version of the app, the system asks the user to grant that permission when the user updates the app. Once the user installs the app, the only way they can revoke the permission is by uninstalling the app.
这里头要注意and和or的使用,说明了只有知足 targetSdkVersion和实际使用设备的版本都在23及以上的时候,才会采用新的动态权限机制. 其余状况, 跟以前同样, 在安装和升级应用的时候就受权了全部的权限.
 
能够总结为:
1.全部的权限都在manifest中声明.
2.若是(1)你的app的targetSdkVersion是23及以上,而且(2)app运行在Android 6.0及以上的设备,危险权限必须动态请求.
当权限被拒绝,app理应仍是可以使用的,只不过权限相关的部分功能不能用.
3.上一条中的两个条件(1)(2)没有同时知足,即属于其余状况, 全部权限在安装时请求,若是用户不接受,则不安装.
 
特别注意这种状况: 旧应用新系统.
若是targetSdkVersion小于23,即被认为是Android 6.0发布以前开发的应用, 尚未兼容6.0.
这种应用即使是被装在Android 6.0的机器上,也是采用原来的安装即授予权限逻辑, 全部权限在应用安装时所有被受权.
在Android 6.0的设备上安装targetSdkVersion小于23的应用以后, 能够在应用的设置中查看,发现全部的dangerous权限状态都是打开. 
因此不用担忧老的应用在Android 6.0上会各类乱崩.
 
可是用户仍然能够在系统设置中禁用权限:
在模拟器上点击受权开关的时候弹出了如下提示:
若是用户执意要取消受权, 应用虽然不会直接崩溃,可是功能变为默默无做为状态, 返回值可能变为null或者0,进而引发没法预料的行为或者崩溃. 
 

为何要及时升级targetSdkVersion

这是由于每个版本的API有可能会产生新的权限,这些新增的权限, 对于targetSdkVersion比该API低的应用是自动获取的.
因此targetSdkVersion最好是能及时写到最新,这样避免应用自动获取到新API新增的权限.
 
结论: 对targetSdkVersion还不存在的权限是自动获取到的.
 
其中”Automatic permission adjustments”那段.
 

Permission group

全部的权限都有本身的permission group.
系统弹框请求某一个permission时也是只说明了它的类别,当用户赞成,系统会给予它该条permission.(只有这一条).
可是若是app已经有了该group下的另外一条permission,系统将会自动授予权限(也即请求权限的callback直接返回),这过程当中不与用户交互.
 

动态权限请求的实现

由于权限动态检查相关的API是Android 6.0才加入的, 因此minSdkVersion不是23时,推荐使用SupportLibrary来实现,好处是: 程序里没必要加if来判断当前设备的版本.

1.检查权限状态

若是执行的操做须要一个dangerous permission, 那么每次在执行操做的地方都必须check你是否有这个permission, 由于用户能够在应用设置里随意地更改受权状况, 因此必须每次在使用前都检查是否有权限.
 
检查权限的方法: ContextCompat.checkSelfPermission()两个参数分别是Context和权限名.
返回值是: PERMISSION_GRANTED if you have the permission, or  PERMISSION_DENIED if not.
 
好比:
if (PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_CONTACTS)) { //has permission, do operation directly ContactsUtils.readPhoneContacts(this); Log.i(DEBUG_TAG, "user has the permission already!"); } else { //do not have permission
 

2.动态请求权限

若是上面权限检查的结果是DENIED, 那么就须要显式地向用户请求这个权限了.
Android提供了几个方法来动态请求权限, 调用这些方法会显示出一个标准的Dialog, 这个Dialog目前是不能被定制的.
 
2.1有时候可能须要解释为何须要这个权限
有时候你可能会须要跟用户解释一下权限的用途.
注意不是每条权限都须要解释,显而易见的那种能够不解释,太多的解释会下降用户体验.
 
一种方式是,当用户拒绝过这个权限,可是又用到了这个功能, 那么极可能用户不是很明白为何app须要这个权限, 这时候就能够先向用户解释一下.
为了发现这种用户可能须要解释的情形, Android提供了一个工具类方法:  shouldShowRequestPermissionRationale()
若是app以前请求过该权限,被用户拒绝, 这个方法就会返回true.
若是用户以前拒绝权限的时候勾选了对话框中”Don’t ask again”的选项,那么这个方法会返回false.
若是设备策略禁止应用拥有这条权限, 这个方法也返回false.
 
注意具体解释缘由的这个dialog须要本身实现, 系统没有提供.
 
2.2请求权限
请求权限的方法是:  requestPermissions() 传入一个Activity, 一个permission名字的数组, 和一个整型的request code.
这个方法是异步的,它会当即返回, 当用户和dialog交互完成以后,系统会调用回调方法,传回用户的选择结果和对应的request code.
代码:
复制代码
if (PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_CONTACTS)) { //has permission, do operation directly ContactsUtils.readPhoneContacts(this); Log.i(DEBUG_TAG, "user has the permission already!"); } else { //do not have permission Log.i(DEBUG_TAG, "user do not have this permission!"); // Should we show an explanation? if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.READ_CONTACTS)) { // Show an explanation to the user *asynchronously* -- don't block // this thread waiting for the user's response! After the user // sees the explanation, try again to request the permission. Log.i(DEBUG_TAG, "we should explain why we need this permission!"); } else { // No explanation needed, we can request the permission. Log.i(DEBUG_TAG, "==request the permission=="); ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_CONTACTS}, MY_PERMISSIONS_REQUEST_READ_CONTACTS); // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an // app-defined int constant. The callback method gets the // result of the request.  } }
复制代码
 
这个对话框是系统的,不能自定义.
经验证, 请求权限对话框中的”Don’t ask again”的选项, 只有该条权限以前的状态是Denied的时候,才会出现.
之前从未受权(即第一次弹框), 或者以前的状态是Granted(固然这种状况通常不会弹框询问), 出现的弹框都是不带该再也不询问的选项的.
 
2.3处理请求权限的响应
当用户对请求权限的dialog作出响应以后,系统会调用 onRequestPermissionsResult() 方法,传回用户的响应.
这个回调中request code即为调用 requestPermissions()时传入的参数,是app自定义的一个整型值.
若是请求取消,返回的数组将会为空.
 
代码:
复制代码
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case MY_PERMISSIONS_REQUEST_READ_CONTACTS: { // 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. ContactsUtils.readPhoneContacts(this); Log.i(DEBUG_TAG, "user granted the permission!"); } else { // permission denied, boo! Disable the // functionality that depends on this permission. Log.i(DEBUG_TAG, "user denied the permission!"); } return; } // other 'case' lines to check for other // permissions this app might request  } }
复制代码
 
 
系统自动回调的状况: 
有一些情形下,调用
1.自动受权: 若是用户已经容许了permission group中的一条A权限,那么当下次调用 requestPermissions()方法请求同一个group中的B权限时, 系统会直接调用 onRequestPermissionsResult() 回调方法, 并传回 PERMISSION_GRANTED的结果.
2.自动拒绝: 若是用户选择了再也不询问此条权限,那么app再次调用 requestPermissions()方法来请求同一条权限的时候,系统会直接调用 onRequestPermissionsResult()回调,返回 PERMISSION_DENIED.
 
 
 

Best Practices

Best Practices 总结:
1.用Intent启动其余应用来完成功能.
2.只用真的须要的权限.
3.不要一次请求多个权限来烦用户,有的权限能够等到要用的时候再请求.
4.向用户解释为何须要这个权限.
5.从Android 6.0开始,每一条权限,都须要测试开关两种状态下是否是都能让应用正常运行,而不是崩溃.
而且相关的权限可能会须要测试不一样的组合.
 

ADB命令

能够用命令行来管理权限:
Use the  adb tool to manage permssions from the command line:
  • List permissions and status by group:

    $ adb shell pm list permissions -d -g html

  • Grant or revoke one or more permissions:
    $ adb shell pm [grant|revoke] <permission-name> ... 

参考资料:

 
 
 
第三方库: 
 
转自:http://www.cnblogs.com/mengdd/p/4892856.html
相关文章
相关标签/搜索