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

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

参考资料:

 
 
 
第三方库: 
 
转自:http://www.cnblogs.com/mengdd/p/4892856.html

Android M Permission 学习笔记的更多相关文章

  1. Android安装器学习笔记(一)

    Android安装器学习笔记(一) 一.Android应用的四种安装方式: 1.通过系统应用PackageInstaller.apk进行安装,安装过程中会让用户确认 2.系统程序安装:在开机的时候自动 ...

  2. android cocos2d-x for Android安装和学习笔记(请用adt-bundle21.1或以上导入)

    引用:http://weimingtom.iteye.com/blog/1483566 (20121108)注意:这篇文章用cdt编译ndk工程的内容已过时(现在可以用adt-bundle,避免配置繁 ...

  3. Android Socket编程学习笔记

    http://blog.csdn.net/eyu8874521/article/details/8847173 度娘给出的描述:通常也称作"套接字",用于描述IP地址和端口,是一个 ...

  4. Android应用开发学习笔记之播放音频

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz Android支持常用音视频格式文件的播放,本文我们来学习怎样开发Android应用程序对音视频进行操作. Andr ...

  5. android 7.0 学习笔记(一)

    导读 增强的Doze模式 后台优化 Data Saver 一.增强的Doze模式 Android N对Android M引进的Doze模式进行了进一步的增强,变化体现在两个方面.一方面是降低了进入Do ...

  6. Android API Guides 学习笔记---Application Fundamentals(一)

    今天开始学习google官网上的API guides ,主要读了Application Fundamentals这一章节,此章节介绍了一个App的基本组成,共包括四大部分内容. 1.      App ...

  7. Android应用开发学习笔记之事件处理

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz Android提供的事件处理机制分为两类:一是基于监听的事件处理:二是基于回调的事件处理.对于基于监听的事件处理,主 ...

  8. Android应用开发学习笔记之Intent

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz Intent是什么呢?来看Android官网上的定义: An intent is an abstractdescri ...

  9. Android应用开发学习笔记之AsyncTask

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz 在上一篇文章中我们学习了多线程和Handler消息处理机制,如果有计算量比较大的任务,可以创建一个新线程执行计算工作 ...

随机推荐

  1. OGLplus 0.33.0 发布,OpenGL 的 C 封装库

    OGLplus 0.33.0 引入很多新的 OGLplus 和 OALplus 示例,更新了构建系统.CamMatrix::LookingAt 构造器.Texture::MaxLevel getter ...

  2. 使用NHibernate(7)-- 一对一 && 一对多 && 多对多

    1, 一对一. 对于数据量比较大的时候,考虑查询的性能,肯能会把一个对象的属性分到两个表中存放:比如用户和用户资料,经常使用的一般是Id和用户名,用户资料(学校,籍贯等)是不经常被查询的,所以就会分成 ...

  3. RabbitMQ(二) -- Work Queues

    RabbitMQ(一) -- Work Queues RabbitMQ使用Work Queues的主要目的是为了避免资源使用密集的任务,它不同于定时任务处理的方式,而是把任务封装为消息添加到队列中.而 ...

  4. [WinAPI] API 6 [操作驱动器挂载点]

    驱动器挂载点,又可以称作卷挂载点.挂载点实际上是操作系统或者用户设置的,用来进入一个逻辑驱动器或者卷的入口.在设置了卷的挂载点后,用户或者应用程序可以使用卷标或者指定的挂载点来进入卷.比如在“C:\” ...

  5. [MFC] 如何更改MFC程序图标

    方法一: >_<:找一张ICO图标,替换programname/res/programname.ico文件,就可以啦,这时候你运行后得到的图标可能还是原来MFC的默认图标,这时候你只要把工 ...

  6. [C++] socket -8 [命名管道]

    ::命名管道不但能实现同一台机器上两个进程通信,还能在网络中不同机器上的两个进程之间的通信机制.与邮槽不同,命名管道是采用基于连接并且可靠的传输方式,所以命名管道传输数据只能一对一进行传输. /* 命 ...

  7. JavaScript中for..in循环陷阱介绍

    for...in循环中的循环计数器是字符串,而不是数字它包含当前属性的名称或当前数组元素的索引,下面有个不错的示例大家可以参考下   大家都知道在JavaScript中提供了两种方式迭代对象: (1) ...

  8. Atitit.如何避免公司破产倒闭的业务魔咒

    Atitit.如何避免公司破产倒闭的业务魔咒 1. 大型公司的衰落或者倒闭破产案例1 1.1. 摩托罗拉1 1.2. 诺基亚2 1.3. sun2 2. 为什么他们会倒闭?? 常见的一些倒闭元素2 2 ...

  9. PAIP.并发编程 多核编程 线程池 ExecutorService的判断线程结束

    PAIP.并发编程 多核编程 线程池 ExecutorService的判断线程结束 ExecutorService并没有提供什么 isDone()或者isComplete()之类的方法. 作者Atti ...

  10. MVC 添加 httpHandlers 支持 .aspx 页面访问

    <?xml version="1.0"?> <!-- For more information on how to configure your ASP.NET ...