Android出于系统稳定性以及用户隐私方面的考虑,将应用程序访问权限限制在各自的沙盒内。程序能够随意访问所在沙盒内部的资源或者信息,访问沙盒外部的则必须明确的申请相关访问权限。应用程序所须要的权限须要在AndroidManifest.xml
文件中申明。如:html
<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... android
系统权限根据敏感程度分为普通权限和危险权限两类。两类权限都须要在AndroidManifest.xml
文件中申明。在Android 5.1 (API level 22) 及其如下版本上,系统在APP安装时要求用户受权全部权限,不然APP不能安装;而在Android 6.0及其以上版本上,系统在APP安装时受权全部普通权限,危险权限须要在使用时动态让用户受权。这使得Android的权限管理更加灵活,用户能够根据须要在设置应用中对应用的各个危险权限授予不一样的权限。Android系统的权限管理不知道被多少人吐槽过,这一改进无疑是加分项。git
Android 6.0上使用在AndroidManifest.xml中已经申明的危险权限时须要用户受权。针对权限请求相关操做系统提供了三个API。下面结合Android官方的开发指导对这几个API作下说明。github
checkSelfPermission() 检查是否已经具备了相关权限。任什么时候候APP都要在执行须要危险权限的操做前去检查是否具备相关权限,即便刚刚执行过这项操做,由于用户颇有可能去设置应用中关闭了相关权限。app
shouldShowRequestPermissionRationale() 判断是否须要向用户解释,为何须要这些权限。有时候用户会不理解应用程序为何须要这些权限。如,相机应用申请摄像头使用权限用户容易理解,可是相机应用申请地理位置使用权限可能会让用户产生疑惑,由于用户颇有能不知道相机须要保存每张照片的拍摄地点。这时候咱们就须要作适当的解释说明了。这个方法只有在APP请求过某一权限且用户禁止APP使用该权限的时候返回true。在用户受权了权限和禁止权限时勾选了“Don't ask again”选项的状况下都会返回false。Android官方开发指导还提到一点,为避免给用户带来糟糕的用户体验,这里的解释说明应该是异步的,不要阻塞用户的操做。时下不少适配了6.0的APP在这点上处理的都不尽如人意,有的根本没有解释说明,有的是弹出对话框,用户体验都不是很好。下文会给出了一个完美的解决方案。框架
requestPermissions() 申请相关权限。调用这个方法后会弹出一个系统对话框来向用户申请权限,APP不能自定义这个对话框的内容,这也就增长了上面提到的解释说明的必要性。这里还有一点也须要交代一下。从上面危险权限列表中也能够看出,这些权限都是有分组的。如,READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE权限就是属于STORAGE组的。分门别类不只仅是为了方便容易阅读,组内权限在申请上也是有关联的。异步
在申请组内某个权限时,弹出的系统对话框会显示组名,而不是指明所申请的权限名。如,申请READ_EXTERNAL_STORAGE权限时,系统对话框提示请求“访问sd卡”权限,但不会说明是请求的sd卡读权限;async
申请权限时,若是组内有别的权限已经得到了用户受权,系统再也不弹出询问对话框,而是自动受权该权限。如,在申请WRITE_EXTERNAL_STORAGE权限时用户已经受权了READ_EXTERNAL_STORAGE权限,系统则会自动受权WRITE_EXTERNAL_STORAGE权限,再也不询问用户;ide
即便有前一条规则存在,在使用每一条权限时都必须(不是应该)调用requestPermissions()
方法来申请权限。如,在已经获取了READ_EXTERNAL_STORAGE权限的状况下,使用WRITE_EXTERNAL_STORAGE权限时依然须要调用requestPermissions()
方法来申请,不然就会由于权限问题致使写sd卡失败。gradle
注意:危险权限在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' }
以上就是Android官方开发指导关于动态权限申请的所有内容了。系统的API已经很简洁了,可是用在项目中时依然会出现不少重复代码,按惯例是要封装一下。在github上搜索"Android permissions"会有不少开源库。Google在github上也开源了一个关于动态权限的封装库easypermissions。看了几个库后,我的以为easypermissions写的最好,可是也有不足的地方。如,解释为何须要权限的地方这个库也弹出一个对话框阻塞了用户操做,不符合开发规范、还有用户禁止权限时勾选了“Don't ask again”后依然会弹出对话框请求权限,用户体验很差。因此我在easypermissions库的基础封装了一个EasyPermissionsEx库。说是一个库,其实只有一个300行左右的类,你能够直接拷贝到你的项目中使用。
前文也提到过在解释为何须要权限或者在用户永久禁止权限后引导用户去设置应用开启权限时,使用对话框会给用户带来很差的体验。EasyPermissionsEx采用的解决方案是使用snackbar来提醒用户。总体效果以下。snackbar是一种轻量级的用户交互,它不会阻塞用户当前的操做,是从底部弹出一个bar来提示用户,相似toast,可是snackbar又有一个button容许用户操做。关于snackbar更多信息能够跳转到官方文档了解。
Snackbar在Android Design Support Library库中记得在build.gradle文件中添加依赖。
dependencies { ... compile 'com.android.support:design:23.4.0' }
EasyPermissionsEx权限请求逻辑以下。关于EasyPermissionsEx更多详情能够去github查看wiki,也能够直接看代码,由于它总共也就300行代码。