关于android6.0动态权限造成的app crash问题
2016-01-28 11:58
267 查看
今天我们为公司内部员工开发的app出现了一个bug,因为我们的app要绑定imei所以我就读取imei并且有个权限READ_PHONE_STATE就是这个权限出现了问题,我就纳闷了怎么会有问题呢,我已经在manifest上声明了,然后我就看一下他的android版本6.0(什么时候国产手机系统跟进竟然这么快了!),我就去搜了一些相关资料才发现是android6.0涉及到隐私的权限是动态的,也就是说你每次调用的方法如果涉及到隐私都需要检查一下该权限,因为用户可以随时取消你得相关授权。
// 假设 thisActivity 是当前的 Activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.WRITE_CALENDAR);
如果该应用已经获取到该权限,该方法返回PackageManager.PERMISSION_GRANTED并且程序可以继续运行。如果该应用未被授予该权限,这个方法会返回PERMISSION_DENIED,同时应用需要明确提示用户该应用所需要的权限。
请求权限
如果你的应用需要敏感权限并且这些敏感权限已经在 manifest 文件中声明,一定要询问用户获取权限。Android 系统提供了几种请求权限的方法。调用这些方法后,系统会弹出一些 Dialog(无需用户自定义)。
解释需要权限的原因
在某些情况下,你可能会想要向用户解释需要权限的原因,比如我要打电话,但是你首先得让我获得你系统的联系人。
Android 系统提供了shouldShowRequestPermissionRationale()方法来帮助开发者判断是否需要向用户解释需要权限的原因。当某条权限之前已经请求过,并且用户已经拒绝了该权限时,shouldShowRequestPermissionRationale ()方法返回的是 true
注意:如果用户拒绝某条权限,并且在提示授权的窗口中勾选了不再提示选项时,shouldShowRequestPermissionRationale ()的返回值为false.当某些设备禁止应用程序获取某些权限时,shouldShowRequestPermissionRationale ()也会返回false
向用户请求获取应用程序需要的权限
如果你的应用程序没有获取到它需要的权限,那么应用程序需要调用该权限对应的requestPermissions()方法,调用requestPermissions()方法时需要传入一个请求码(requestCode),这时系统会弹出一个对话框让用户选择是否授权,用户选择后,在回调方法onRequestPermissionsResult()中返回对应的请求码(requestCode)和授权结果。
下面这段代码 检查应用程序是否有读联系人权限,在未获取读联系人授权时请求获取该权限(完整示例见Android_M_Permission):
处理请求权限的结果
当应用程序请求获取权限时,系统会弹出一个对话框给用户。当用户点击某个选项时,系统会调用onRequestPermissionsResult()方法来传递用户的选择结果。应用程序需要重写onRequestPermissionsResult()方法来判断用户是否对相应权限授权。。这个回调方法会传递一个与requestPermission()方法相同的requestCode.例如,应用程序请求READ_CONTACTS方法,它将会有如下的回调方法:
注意:应用程序还是需要明确的请求它所需要的每个权限,即使用户已经授予了跟这个权限在同一个permission group的其他权限。除此之外,对某个权限组的授权可能会改变。程序的代码不能依赖于用户已经对某个权限组授权的假设。
例如,应用程序在 manifest 文件用声明了READ_CONTACTS和WRITE_CONTACTS权限,如果应用程序请求了READ_CONTACTS权限并且用户授予了该权限,那么当应用程序请求WRITE_CONTACTS权限时,系统会自动授予应用程序该权限。
译者注:READ_CONTACTS和WRITE_CONTACTS都属于CONTACTS权限组。更多关于权限组信息可以访问permission group或直接看我的截图:权限和权限组
如果用户拒绝了一个应用权限请求,那么应用程序应该进行适当的操作。例如:应用程序可以弹出一个对话框来解释为什么用户不能执行需要该权限的操作。
当系统提示用户给应用程序授权权限时,会给用户提供一个不再提示的选项来通知系统不再针对该权限进行询问。用户勾选该选项后,当应用程序请求获取对应权限时,系统会立即拒绝授权。系统会调用onRequestPermissionResult()回调方法并且传递PERMISSION_DENIED参数,就像用户拒绝授权一样。这意味着,当你调用requestPermissions()方法时,你不能假定应用程序会跟用户直接交互。
相关参考:
http://kingideayou.github.io/2015/11/09/Android_M_permissions/
权限检查
你可以通过ContextCompat.checkSelfPermission()方法来验证你的应用是否拥有某个权限。比如,下面的代码段是检查是否有拥有写日历权限:// 假设 thisActivity 是当前的 Activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.WRITE_CALENDAR);
如果该应用已经获取到该权限,该方法返回PackageManager.PERMISSION_GRANTED并且程序可以继续运行。如果该应用未被授予该权限,这个方法会返回PERMISSION_DENIED,同时应用需要明确提示用户该应用所需要的权限。
请求权限
如果你的应用需要敏感权限并且这些敏感权限已经在 manifest 文件中声明,一定要询问用户获取权限。Android 系统提供了几种请求权限的方法。调用这些方法后,系统会弹出一些 Dialog(无需用户自定义)。
解释需要权限的原因
在某些情况下,你可能会想要向用户解释需要权限的原因,比如我要打电话,但是你首先得让我获得你系统的联系人。
Android 系统提供了shouldShowRequestPermissionRationale()方法来帮助开发者判断是否需要向用户解释需要权限的原因。当某条权限之前已经请求过,并且用户已经拒绝了该权限时,shouldShowRequestPermissionRationale ()方法返回的是 true
注意:如果用户拒绝某条权限,并且在提示授权的窗口中勾选了不再提示选项时,shouldShowRequestPermissionRationale ()的返回值为false.当某些设备禁止应用程序获取某些权限时,shouldShowRequestPermissionRationale ()也会返回false
向用户请求获取应用程序需要的权限
如果你的应用程序没有获取到它需要的权限,那么应用程序需要调用该权限对应的requestPermissions()方法,调用requestPermissions()方法时需要传入一个请求码(requestCode),这时系统会弹出一个对话框让用户选择是否授权,用户选择后,在回调方法onRequestPermissionsResult()中返回对应的请求码(requestCode)和授权结果。
下面这段代码 检查应用程序是否有读联系人权限,在未获取读联系人授权时请求获取该权限(完整示例见Android_M_Permission):
// thisActivity 为当前 Activity // 检查是否已经授权该权限 if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { // 判断是否需要解释获取权限原因 if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity, Manifest.permission.READ_CONTACTS)) { // 需要向用户解释 // 此处可以弹窗或用其他方式向用户解释需要该权限的原因 } else { // 无需解释,直接请求权限 ActivityCompat.requestPermissions(thisActivity, new String[]{Manifest.permission.READ_CONTACTS}, MY_PERMISSIONS_REQUEST_READ_CONTACTS); // MY_PERMISSIONS_REQUEST_READ_CONTACTS 是自定义的常量,在回调方法中可以获取到 } }注意:当应用程序调用requestPermissions()方法时,系统会弹出一个对话框给用户。应用程序不能设置或更改该对话框,如果应用程序需要提供一些信息或者向用户解释,需要在调用requestPermissions()方法之前,如Explain why the app needs permissions所述。
处理请求权限的结果
当应用程序请求获取权限时,系统会弹出一个对话框给用户。当用户点击某个选项时,系统会调用onRequestPermissionsResult()方法来传递用户的选择结果。应用程序需要重写onRequestPermissionsResult()方法来判断用户是否对相应权限授权。。这个回调方法会传递一个与requestPermission()方法相同的requestCode.例如,应用程序请求READ_CONTACTS方法,它将会有如下的回调方法:
@Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case MY_PERMISSIONS_REQUEST_READ_CONTACTS: { //如果请求被取消,那么 result 数组将为空 if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 已经获取对应权限 // TODO 执行相应的操作联系人的方法 } else { // 未获取到授权,取消需要该权限的方法 } return; } // 检查其他权限...... } }授权的对话框显示的是系统描述的权限组(permission group),它没有显示列出详细的权限列表。比如,如果你请求READ_CONTACTS权限,系统对话框只会提示用户应用程序需要获取联系人权限,用户只需要给每个权限组授权一次。如果应用程序请求获取一个权限组的其他权限(在 manifest 文件中声明的权限),系统会自动授予该权限。当你请求这个权限时,系统会调用onRequestPermissionsResult()回调方法并且传递PERMISSION_GRANTED,这跟用户在弹窗中点击授予权限的按钮的流程是相同的。
注意:应用程序还是需要明确的请求它所需要的每个权限,即使用户已经授予了跟这个权限在同一个permission group的其他权限。除此之外,对某个权限组的授权可能会改变。程序的代码不能依赖于用户已经对某个权限组授权的假设。
例如,应用程序在 manifest 文件用声明了READ_CONTACTS和WRITE_CONTACTS权限,如果应用程序请求了READ_CONTACTS权限并且用户授予了该权限,那么当应用程序请求WRITE_CONTACTS权限时,系统会自动授予应用程序该权限。
译者注:READ_CONTACTS和WRITE_CONTACTS都属于CONTACTS权限组。更多关于权限组信息可以访问permission group或直接看我的截图:权限和权限组
如果用户拒绝了一个应用权限请求,那么应用程序应该进行适当的操作。例如:应用程序可以弹出一个对话框来解释为什么用户不能执行需要该权限的操作。
当系统提示用户给应用程序授权权限时,会给用户提供一个不再提示的选项来通知系统不再针对该权限进行询问。用户勾选该选项后,当应用程序请求获取对应权限时,系统会立即拒绝授权。系统会调用onRequestPermissionResult()回调方法并且传递PERMISSION_DENIED参数,就像用户拒绝授权一样。这意味着,当你调用requestPermissions()方法时,你不能假定应用程序会跟用户直接交互。
相关参考:
http://kingideayou.github.io/2015/11/09/Android_M_permissions/
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories