Android6.0之运行时权限
2016-01-13 19:46
351 查看
Android 6.0来了
Android 6.0 Marshmallow新增了运行时权限特性,还没有适配6.0系统的应用可能有点猝不及防了。这不用户反馈新买的最贵的华为手机打不开我们的应用。报的是权限异常,那就来看看android6.0新的权限特性到底是什么。墙外的官方文档
自从developer.android.com无法访问后就很少看android官方文档了。但是网上的博文毕竟是别人消化过的,变味了。其实完全可以阅读离线文档。我目前的开发环境是android studio + sdk代理,更新到sdk23版本,在sdk/doc目录下就是离线文档。再用火狐浏览器打开,脱机工作就大功告成了。
简介
通过主页Android6.0 Marshmallow展示的”Get started”链接跳到review页面,选择Behavior changes项,找到Runtime Permissions,简略翻译如下:新的权限模型,允许用户在应用运行时直接管理应用权限。
如果你的应用targetSdkVersion是M Preview发布版或更高版本,请确保在运行时检查并获取权限。即使你的目标sdk不是M Preview发布版,也应该在新的权限模型下进行测试。
调用checkSelfPermission()方法确定权限是否已获取,调用requestPermissions去获取权限。
更多支持信息请参考Permissions页面。关于对应用的影响,请参考TestingGuide。
(译者注:这里有个坑,如果你在app/build.gradle和AndroidManifest.xml都配置了targetSdkVersion请确保二者统一,如果不统一,AndroidManifest.xml的配置会被app/build.gradle的配置覆盖。)
概述
点击上面的Permissions链接,跳到Preview-API Overview-Permissions,看一下详细内容:新的权限系统兼容低版本sdk。
新的模型包含下面的特性:
在manifest声明权限:像早的平台一样;
权限组:权限被分到权限组里。比如CONTACTS权限组包含了读取和写入用户通讯录和个人信息的权限。
安装时特定权限:当用户安装或更新应用程序时,系统授予所有列在manifest里的级别为PROTECTION_NORMAL的权限。比如闹钟和因特网权限是PROTECTION_NORMAL级别权限,所以会在安装时被自动授予。
更多信息请参考Normal Permissions。
系统也会给一个应用程序授予签名权限,在System components and signature Permissions里有描述。在安装时不会提示签名权限。
用户运行时权限授予:当应用程序请求一个权限时,系统为用户展示一个对话框,然后调用应用程序的回调方法通知应用程序用户是否授予了该权限。
这个权限模型改变了你的应用程序使用需要权限的特性的方式。下面是你需要对此作出的调整。
总是检查权限:在应用程序进行需要权限的操作时,应该首先检查是否拥有这个权限。如果没有,需要请求授予权限。PROTECTION_NORMAL级别的权限除外。
优雅的处理权限缺失:如果应用程序没有被授予特定的权限,它必须明确的处理失败。比如,某个特性需要一项未被授予的权限,应用程序可以禁用它。如果非要使用这个权限,应用程序需要把相关的所有功能设为不可用,并且提示用户需要授予该权限。
权限是可撤销的:用户可以在任何时候撤销权限。如果用户关闭了一项应用权限,应用程序不会被通知到。你的应用程序需要在下次使用时再次确认是否拥有该权限。
权限组
相关的权限被分在权限组中,这样用户可以通过一次授权完成对组中所有权限的授权。用户只需要对应用程序授权一项权限,那么随后相同权限组中的权限会被自动授予。比如,假设应用程序的manifest中包含了SEND_SMS和RECEIVE_SMS两项权限,它们属于相同的权限组android.permission-group.SMS。当应用需要发送一条短信时,会请求SEND_SMS权限。系统展示给用户请求授权访问SMS的权限。如果用户同意了,系统会授予应用SEND_SMS权限。随后,当应用请求RECEIVE_SMS权限。系统会自动授予这个权限,因为用户已经批准了相同权限组中的权限。系统组件以及签名权限
通常当用户安装应用时,系统只授权给应用manifest中PROTECTION_NORMAL级别的权限。但是下面的几种情况系统会授予应用更多的权限:
系统组件自动被授予manifest中的所有权限。
如果应用的manifect中请求的权限是PROTECTION_SIGNATURE级别的权限,并且应用程序的签名证书与声明该权限的应用程序的签名证书一致,系统会在安装时授予应用程序这些权限。应用程序不能在运行时请求签名权限。
向前和向后兼容
如果应用程序目标sdk不是M版,应用程序继续在M版本设备上使用旧的权限模型。当用户安装应用程序时,系统会询问用户授权所有列在manifest里的权限。注意:在运行M版本的设备上,用户能通过设置关闭任何应用的权限(包括旧的应用)。如果用户关闭了旧应用的权限,系统会默认禁用相应的功能。当应用程序试图进行需要这些权限的操作时,这个操作不一定会引发异常。相反的,它可能返回空的数据集、错误信号或者产生未预料的行为。比如,如果你在没有权限的情况下查看日历,会返回空的数据集。
如果你的应用运行在不是M版本的设备上,系统会像对待其他应用一样对待它:系统会在安装时询问用户声明的授权权限。
权限和Intent
一般情况下你有两种方式来完成一项任务。你可以请求被授予权限来完成这件事,或者通过发送intent给其他的应用来完成。比如,假设你的应用需要调用相机拍摄一张图片。你的应用可以获取android.permission.CAMERA权限,这样你就可以直接访问相机,并调用相机api来
控制相机拍照。这样你可以控制拍照的全部过程并自己定义相机UI。
如果你不需要这样的控制,你可以使用ACTION_IMAGE_CAPTURE这个intent来获取图片。当你启动这个intent,会有选择相机应用的提示框提示给用户,用户可以通过这个相机应用拍照,相机应用通过你的onActivityResult()方法返回结果。
同样的,如果你想打电话、访问用户通讯录等等,你都可以通过创建相应的intent或者直接获取权限的方式。两种方式各有优劣。
如果你用权限:
可以完全控制用户体验,但是会增加任务的复杂性。
第一次的时候回提示用户获取权限。如果用户禁止了权限,你的应用就不能进行这项操作了。
如果你用intent:
不需要自己定制ui。缺点是不能控制用户体验。
如果没有指定默认的应用,每次都会弹出选择框。
运行时权限编码
启用新的权限模型将应用的targetSDKVersion设置为”MNC”,compileSDKVersion设置为”android-MNC”,你就启用了M开发版。必须将minSdkVersion指定为”MNC”才能启用发布版。
指定只针对M版的权限
在manifest中使用新的
<users-permission-sdk-m>
标签来表明该权限只针对M版。这样声明后,如果应用在老的设备上安装,系统不会提醒用户授权该项权限。安装更新也是一样。
在M版本上
<users-permission-sdk-m>
跟
<users-permission>
是一样的。
权限弹窗
检查应用运行在哪个平台上
新的权限模型只能用在M版设备上。在调用这些权限方法时必须判断Build.VERSION.CODENAME是否是”MNC”。
检查当前是否有某项权限时使用
Context.checkSelfPermission(permission_name)方法。比如检查拍照权限: Context.checkSelfPermission(Manifest.permission.CAMERA).
权限和权限组如下:
解释为什么需要权限
在一些情况下你也许需要帮助用户理解为什么需要一项权限。比如,如果用户登录了一个摄影应用,用户不会对它需要相机权限感到惊讶。但是如果用户关闭了这项权限,然后再次登录这个摄影应用,这时可能表明需要帮助用户理解为什么需要这项权限。
为了找出哪里需要做出额外的权限解释,系统提供了
Activity.shouldShowRequestPermissionRationale(String)方法。如果用户在授权提示框里拒绝了授权请求,这个方法会返回true。表明这里需要为用户做出解释。
如果用户在关闭权限时选择了“不再提醒”,这个方法会返回false。如果设备策略禁止了应用的这项权限同样会返回false。
请求权限
如果应用没有它需要的权限可以通过Activity.requestPermissions(String[], int)方法获取。参数是要获取的权限及请求码。这个请求是异步的:它立即返回并展示对话框,用户操作对话框后系统调用回调方法返回结果及跟requestPermissions相同的返回码。
if(checkSelfPermission(Manifest.permission.READ_CONTACTS) !=PackageManager.PERMISSION_GRANTED){ // Should we show an explanation? if(shouldShowRequestPermissionRationale( Manifest.permission.READ_CONTACTS)){ // Explain to the user why we need to read the contacts } requestPermissions(newString[]{Manifest.permission.READ_CONTACTS}, MY_PERMISSIONS_REQUEST_READ_CONTACTS); // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an // app-defined int constant return; }
处理权限响应
用户选择后的系统回调方法是Activity.onRequestPermissonResult(int, String[], int[])。你的应用需要覆写这个方法。比如:如果你请求READ_CONTACTS权限,你可以这样处理:
publicvoid onRequestPermissionsResult(int requestCode, String permissions[],int[] grantResults){ switch(requestCode){ case MY_PERMISSIONS_REQUEST_READ_CONTACTS:{ if(grantResults[0]==PackageManager.PERMISSION_GRANTED){ // permission was granted, yay! do the // calendar task you need to do. }else{ // permission denied, boo! Disable the // functionality that depends on this permission. } return; } // other 'switch' lines to check for other // permissions this app might request } }
如果用户拒绝了权限请求,你可以采取适当的操作。比如:你可以弹出对话框解释为什么不能进行原来的操作。
当系统请求用户授权权限时,用户可以选择不再提醒。这种情况下,当应用调用requestPermissions()请求权限时,系统会立即拒绝该请求,并调用onRequestPermiResult()方法。所以,应用不能假设每次权限请求都会跟用户交互。
测试运行时权限
当你的应用目标sdk是M版本时,你必须处理好权限。你不能假设应用运行时拥有了某项权限。当应用运行时,很可能没有任何权限,用户可以在任何时候撤销或恢复权限。你必须保证你的应用在任何权限状况下都表现良好。
新的adb命令和选项
M版本提供了几个新的命令来测试应用处理权限的场景。你可以用adb install 的-g选项来授权manifest里的所有权限。$ adb install -g <path_to_apk>
你可以用新的pm命令来授权或撤销权限。可以将它用于自动化测试。
使用grant命令授权权限:
$ adb pm grant <package_name> <permission_name>
比如,授权com.example.myapp录音权限:
$ adb pm grant com.example.myapp android.permission.RECORD_AUDIO
使用revoke命令撤销权限:
$ adb pm revoke <package_name> <permission_name>
最佳实践及使用说明
新的权限模型提供了更顺畅的用户体验,也让用户能够更放心的安装应用并了解应用正在做的事情。下面是推荐的使用方式。只请求你需要的权限
每次请求权限,你都在强迫用户做选择。如果用户拒绝了请求会削弱你的应用的功能。
你应该尽量少的做请求。
比如:通常你能够使用intent而不是权限来完成某项任务。
不要强迫用户
如果你一次请求了过多的权限,你可能在强迫用户并导致用户退出你的应用。相反的,你应该在需要的时候去请求权限。有些情况下,应用确实需要多个权限,这时,你应该在应用启动时尽早去请求权限。如果你的应用提供了教程,那么在教程的最后对必要的权限进行说明是很有必要的。
Normal Permissions
很多权限被定义为PROTECTION_NORMAL,这表明他们对用户隐私和安全造成的风险很小。比如,用户会很想知道一个应用是否会读取通讯录信息。相反,振动设备不会有太大的风险,所以它被指定为normal。
如果你在manifest里声明了normal级别的权限,你不需要调用
Activity.checkShelfPermission()或者Activity.requestPermissions方法。
目前PROTECTION_NORMAL级别的权限如下:
android.permission.ACCESS_LOCATION_EXTRA_COMMANDS
android.permission.ACCESS_NETWORK_STATE
android.permission.ACCESS_WIFI_STATE
android.permission.ACCESS_WIMAX_STATE
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
android.permission.BROADCAST_STICKY
android.permission.CHANGE_NETWORK_STATE
android.permission.CHANGE_WIFI_MULTICAST_STATE
android.permission.CHANGE_WIFI_STATE
android.permission.DISABLE_KEYGUARD
android.permission.EXPAND_STATUS_BAR
android.permission.FLASHLIGHT
android.permission.GET_ACCOUNTS
android.permission.GET_PACKAGE_SIZE
android.permission.INTERNET
android.permission.KILL_BACKGROUND_PROCESSES
android.permission.MODIFY_AUDIO_SETTINGS
android.permission.NFC
android.permission.PERSISTENT_ACTIVITY
android.permission.READ_SYNC_SETTINGS
android.permission.READ_SYNC_STATS
android.permission.READ_USER_DICTIONARY
android.permission.RECEIVE_BOOT_COMPLETED
android.permission.REORDER_TASKS
android.permission.SET_TIME_ZONE
android.permission.SET_WALLPAPER
android.permission.SET_WALLPAPER_HINTS
android.permission.SUBSCRIBED_FEEDS_READ
android.permission.TRANSMIT_IR
android.permission.VIBRATE
android.permission.WAKE_LOCK
android.permission.WRITE_SETTINGS
android.permission.WRITE_SYNC_SETTINGS
android.permission.WRITE_USER_DICTIONARY
com.android.alarm.permission.SET_ALARM
com.android.launcher.permission.INSTALL_SHORTCUT
--------全文完--------
相关文章推荐
- 使用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