Android 6.0权限全面详细分析和解决方案- 屌丝迷途 - 博客园
文章推薦指數: 80 %
所以为了应对这种情况,系统提供了一个shouldShowRequestPermissionRationale()函数,这个函数的作用是帮助开发者找到需要向用户额外解释权限的情况, ...
首页
新闻
博问
专区
闪存
班级
我的博客
我的园子
账号设置
简洁模式...
退出登录
注册
登录
屌丝迷途
Android6.0权限全面详细分析和解决方案
原文:
http://www.2cto.com/kf/201512/455888.html
http://blog.csdn.net/yangqingqo/article/details/48371123
http://inthecheesefactory.com/blog/things-you-need-to-know-about-Android-m-permission-developer-edition/en
一、Marshmallow版本权限简介
android的权限系统一直是首要的安全概念,因为这些权限只在安装的时候被询问一次。
一旦安装了,app可以在用户毫不知晓的情况下访问权限内的所有东西,而且一般用户安装的时候很少会去仔细看权限列表,更不会去深入了解这些权限可能带来的相关危害。
所以在android6.0Marshmallow版本之后,系统不会在软件安装的时候就赋予该app所有其申请的权限,对于一些危险级别的权限,app需要在运行时一个一个询问用户授予权限。
二、旧版本app兼容问题 那么问题来了,是不是所有以前发布的app都会出现问题呢?答案是不会,只有那些targetSdkVersion设置为23和23以上的应用才会出现异常,在使用危险权限的时候系统必须要获得用户的同意才能使用,要不然应用就会崩溃,出现类似java.lang.SecurityException:PermissionDenial:readingcom.android.providers.media.MediaProvider
的崩溃日志。
所以targetSdkVersion如果没有设置为23版本或者以上,系统还是会使用旧规则:在安装的时候赋予该app所申请的所有权限。
所以app当然可以和以前一样正常使用了,但是还有一点需要注意的是6.0的系统里面,用户可以手动将该app的权限关闭,如下图
那么问题又来了,如果以前的老应用申请的权限被用户手动关闭了怎么办,应用会崩溃么?我们来试一试 这里写图片描述 好吧,可以庆幸了一下了,不会抛出异常,不会崩溃,只不过调用那些被用户禁止权限的api接口返回值都为null或者0,所以我们只需要做一下判空操作就可以了,不判空当然还是会崩溃的喽。
三、普通权限和危险权限列表
现在对于新版本的权限变更应该有了基本的认识,那么,是不是所有权限都需要去进行特殊处理呢?当然不是,只有那些危险级别的权限才需要。
PROTECTION_NORMAL类权限
当用户安装或更新应用时,系统将授予应用所请求的属于PROTECTION_NORMAL的所有权限(安装时授权的一类基本权限)。
这类权限包括:
android.permission.ACCESS LOCATIONEXTRA_COMMANDS android.permission.ACCESS NETWORKSTATE android.permission.ACCESS NOTIFICATIONPOLICY android.permission.ACCESS WIFISTATE android.permission.ACCESS WIMAXSTATE android.permission.BLUETOOTH android.permission.BLUETOOTH_ADMIN android.permission.BROADCAST_STICKY android.permission.CHANGE NETWORKSTATE android.permission.CHANGE WIFIMULTICAST_STATE android.permission.CHANGE WIFISTATE android.permission.CHANGE WIMAXSTATE android.permission.DISABLE_KEYGUARD android.permission.EXPAND STATUSBAR android.permission.FLASHLIGHT android.permission.GET_ACCOUNTS android.permission.GET PACKAGESIZE android.permission.INTERNET android.permission.KILL BACKGROUNDPROCESSES android.permission.MODIFY AUDIOSETTINGS android.permission.NFC android.permission.READ SYNCSETTINGS android.permission.READ SYNCSTATS android.permission.RECEIVE BOOTCOMPLETED android.permission.REORDER_TASKS android.permission.REQUEST INSTALLPACKAGES android.permission.SET TIMEZONE android.permission.SET_WALLPAPER android.permission.SET WALLPAPERHINTS android.permission.SUBSCRIBED FEEDSREAD android.permission.TRANSMIT_IR android.permission.USE_FINGERPRINT android.permission.VIBRATE android.permission.WAKE_LOCK android.permission.WRITE SYNCSETTINGS com.android.alarm.permission.SET_ALARM com.android.launcher.permission.INSTALL_SHORTCUT com.android.launcher.permission.UNINSTALL_SHORTCUT
这类权限只需要在AndroidManifest.xml中简单声明这些权限就好,安装时就授权。
不需要每次使用时都检查权限,而且用户不能取消以上授权。
危险权限
PermissionGroupPermissions
android.permission-group.CALENDAR
android.permission.READ_CALENDAR
android.permission.WRITE_CALENDAR
android.permission-group.CAMERA
android.permission.CAMERA
android.permission-group.CONTACTS
android.permission.READ_CONTACTS
android.permission.WRITE_CONTACTS
android.permission.GET_ACCOUNTS
android.permission-group.LOCATION
android.permission.ACCESS_FINE_LOCATION
android.permission.ACCESS_COARSE_LOCATION
android.permission-group.MICROPHONE
android.permission.RECORD_AUDIO
android.permission-group.PHONE
android.permission.READ_PHONE_STATE
android.permission.CALL_PHONE
android.permission.READ_CALL_LOG
android.permission.WRITE_CALL_LOG
com.android.voicemail.permission.ADD_VOICEMAIL
android.permission.USE_SIP
android.permission.PROCESS_OUTGOING_CALLS
android.permission-group.SENSORS
android.permission.BODY_SENSORS
android.permission-group.SMS
android.permission.SEND_SMS
android.permission.RECEIVE_SMS
android.permission.READ_SMS
android.permission.RECEIVE_WAP_PUSH
android.permission.RECEIVE_MMS
android.permission.READ_CELL_BROADCASTS
android.permission-group.STORAGE
android.permission.READ_EXTERNAL_STORAGE
android.permission.WRITE_EXTERNAL_STORAGE
android开发者官网也有相关描述: http://developer.android.com/training/permissions/requesting.html http://developer.android.com/guide/topics/security/permissions.html
所以仔细去看看自己的app,对照列表,如果有需要申请其中的一个权限,就需要进行特殊操作。
还有一个比较人性的地方就是如果同一组的任何一个权限被授权了,其他权限也自动被授权。
例如,一旦WRITE_EXTERNAL_STORAGE被授权了,app也有READ_EXTERNAL_STORAGE权限了。
四、支持Marshmallow新版本权限机制
关于权限控制主要使用到
PermissionChecker类的checkSelfPermission();
ActivityCompat类的
publicstaticbooleanshouldShowRequestPermissionRationale(@NonNullActivityactivity, @NonNullStringpermission)
Fragment类的
publicbooleanshouldShowRequestPermissionRationale(@NonNullStringpermission)
ActivityCompat类的
publicstaticvoidrequestPermissions(final@NonNullActivityactivity, final@NonNullString[]permissions,finalintrequestCode)
Fragment类的
publicfinalvoidrequestPermissions(@NonNullString[]permissions,intrequestCode)
终于要开始支持android6.0版本了,最先一步当然就是修改build.gradle文件中的tragetSdkVersion和compileSdkVersion成23版本,同时使用compile‘com.android.support:appcompat-v7:23.1.1’最新v7包。
android{ compileSdkVersion23 ... defaultConfig{ ... targetSdkVersion23 ... }}...dependencies{...compile'com.android.support:appcompat-v7:23.1.1' 修改完后,感兴趣的朋友可以直接打包在手机上测试一下,看看是不是会出现类似于上面我说的那些崩溃日志。
接着下一步当然就是要修改代码了,最原始代码,无任何处理:privatevoidstartGetImageThread(){.... Uriuri=MediaStore.Images.Media.EXTERNAL_CONTENT_URI; ContentResolvercontentResolver=getContentResolver(); //获取jpeg和png格式的文件,并且按照时间进行倒序 Cursorcursor=contentResolver.query(uri,null,MediaStore.Images.Media.MIME_TYPE+"=\"image/jpeg\"or"+ MediaStore.Images.Media.MIME_TYPE+"=\"image/png\"",null,MediaStore.Images.Media.DATE_MODIFIED+"desc"); ....} 这段代码需要访问外部存储(相册图片),属于危险级别的权限,直接使用会造成应用崩溃,所以在这段代码执行之前我们需要进行特殊处理:inthasWriteContactsPermission=checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
if(hasWriteContactsPermission!=PackageManager.PERMISSION_GRANTED){
Activityactivty=this;
ActivityCompat.requestPermissions(activty,newString[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, CODE_FOR_WRITE_PERMISSION); return;} 写完这段代码之后,就会出现如下系统dialog: 紧接着就需要去处理DENY和ALLOW的回调了,重写 Activityactivity的ActivityCompat.OnRequestPermissionsResultCallback函数:@OverridepublicvoidonRequestPermissionsResult(intrequestCode,String[]permissions,int[]grantResults){ if(requestCode==CODE_FOR_WRITE_PERMISSION){ if(permissions[0].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE) &&grantResults[0]==PackageManager.PERMISSION_GRANTED){ //用户同意使用write startGetImageThread(); }else{ //用户不同意,自行处理即可 finish(); } }} 好了,这样就算是简单初步适配完成了。
五、处理不再提醒 如果用户拒绝某授权。
下一次弹框,用户会有一个“不再提醒”的选项的来防止app以后继续请求授权。
如果这个选项在拒绝授权前被用户勾选了。
下次为这个权限请求requestPermissions时,对话框就不弹出来了,系统会直接回调onRequestPermissionsResult函数,回调结果为最后一次用户的选择。
所以为了应对这种情况,系统提供了一个shouldShowRequestPermissionRationale()函数,这个函数的作用是帮助开发者找到需要向用户额外解释权限的情况,这个函数:应用安装后第一次访问,直接返回false;第一次请求权限时,用户拒绝了,下一次shouldShowRequestPermissionRationale()返回true,这时候可以显示一些为什么需要这个权限的说明;第二次请求权限时,用户拒绝了,并选择了“不再提醒”的选项时:shouldShowRequestPermissionRationale()返回false;设备的系统设置中禁止当前应用获取这个权限的授权,shouldShowRequestPermissionRationale()返回false; 注意:第二次请求权限时,才会有“不再提醒”的选项,如果用户一直拒绝,并没有选择“不再提醒”的选项,下次请求权限时,会继续有“不再提醒”的选项,并且shouldShowRequestPermissionRationale()也会一直返回true。
所以利用这个函数我们可以进行相应的优化,针对shouldShowRequestPermissionRationale函数返回false的处理有两种方案。
第一种方案:如果应用是第一次请求该权限,则直接调用requestPermissions函数去请求权限;如果不是则代表用户勾选了’不再提醒’,弹出dialog,告诉用户为什么你需要该权限,让用户自己手动开启该权限。
链接:http://stackoverflow.com/questions/32347532/android-m-permissions-confused-on-the-usage-of-shouldshowrequestpermissionrati。
第二种方案:在onRequestPermissionsResult函数中进行检测,如果返回PERMISSION_DENIED,则去调用shouldShowRequestPermissionRationale函数,如果返回false代表用户已经禁止该权限(上面的3和4两种情况),弹出dialog告诉用户你需要该权限的理由,让用户手动打开。
链接:http://stackoverflow.com/questions/30719047/android-m-check-runtime-permission-how-to-determine-if-the-user-checked-nev 处理方法已经有了,修改一下代码,我这里就以第二种方案来处理了: @OverridepublicvoidonRequestPermissionsResult(intrequestCode,String[]permissions,int[]grantResults){ if(requestCode==CODE_FOR_WRITE_PERMISSION){ if(permissions[0].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE) &&grantResults[0]==PackageManager.PERMISSION_GRANTED){ //用户同意使用write startGetImageThread(); }else{ //用户不同意,向用户展示该权限作用 if(!ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)){ AlertDialogdialog=newAlertDialog.Builder(this) .setMessage("该相册需要赋予访问存储的权限,不开启将无法正常工作!") .setPositiveButton("确定",newDialogInterface.OnClickListener(){ @Override publicvoidonClick(DialogInterfacedialog,intwhich){ finish(); } }) .setNegativeButton("取消",newDialogInterface.OnClickListener(){ @Override publicvoidonClick(DialogInterfacedialog,intwhich){ finish(); } }).create(); dialog.show(); return; } finish(); } }} 当勾选不再提醒,并且拒绝之后,弹出dialog,提醒用户该权限的重要性: 六、使用兼容库 以上的代码在6.0版本上使用没有问题,但是在之前就有问题了,最简单粗暴的解决方法可能就是利用Build.VERSION.SDK_INT>=23这个判断语句来判断了,方便的是SDK23的v4包加入了专门类进行相关的处理:ContextCompat.checkSelfPermission()被授权函数返回PERMISSION_GRANTED,否则返回PERMISSION_DENIED,在所有版本都是如此。
ActivityCompat.requestPermissions()这个方法在6.0之前版本调用,OnRequestPermissionsResultCallback直接被调用,带着正确的PERMISSION_GRANTED或者PERMISSION_DENIED。
ActivityCompat.shouldShowRequestPermissionRationale()在6.0之前版本调用,永远返回false。
用v4包的这三方法,完美兼容所有版本!下面是代码://使用兼容库就无需判断系统版本inthasWriteContactsPermission=ContextCompat.checkSelfPermission(getApplication(),Manifest.permission.WRITE_EXTERNAL_STORAGE);if(hasWriteContactsPermission==PackageManager.PERMISSION_GRANTED){ startGetImageThread();}//需要弹出dialog让用户手动赋予权限else{ ActivityCompat.requestPermissions(PickOrTakeImageActivity.this,newString[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},CODE_FOR_WRITE_PERMISSION);
}
onRequestPermissionsResult函数不变。
后两个方法,我们也可以在Fragment中使用,用v13兼容包:FragmentCompat.requestPermissions()andFragmentCompat.shouldShowRequestPermissionRationale()和activity效果一样。
七、一次请求多个权限 当然了有时候需要多个权限,可以用上面方法一次请求多个权限。
当然最重要的是不要忘了为每个权限检查“不再提醒”的设置。
List
八、第三方库简化代码 当然早就有第三方库来帮忙做这些事情了: Github上的开源项目PermissionHelper和hotchemi’sPermissionsDispatcher九、APP处于运行状态下,被撤销权限 如果APP正在运行中,用户进入设置-应用程序页面去手动撤销该APP权限,会出现什么情况呢?哈哈,系统又会接着弹出权限请求对话框,挺好挺好: 这样就没有问题了吧O(∩_∩)O~ 上面的测试环境为genymotion6.0模拟器,有朋友跟我反映在6.0nexus6p真机上会直接退出应用,所以这个应该还和测试环境有关。
使用兼容库support-v4中的方法
ContextCompat.checkSelfPermission()
ActivityCompat.requestPermissions()
ActivityCompat.OnRequestPermissionsResultCallback
ActivityCompat.shouldShowRequestPermissionRationale()
Thev4supportlibraryalsocontainsthe PermissionChecker class,whichprovidesseveralstaticutilitymethodsforappsthatuseIPCtoprovideservicesforotherapps.Forexample,PermissionChecker.checkCallingPermission() checkswhetheranIPCmadebyaparticularpackagehasaspecifiedpermission.
FragmentCompat.requestPermissions()
FragmentCompat.shouldShowRequestPermissionRationale()
requestPermissions() 的一些说明:
Note: Whenyourappcallstheframework's requestPermissions() method,thesystemshowsastandarddialogboxtotheuser.
Yourapp cannot configureoralterthatdialogbox.Ifyouneedtoprovideanyinformationorexplanationtotheuser,
youshoulddothat before youcall requestPermissions(),asdescribedin Explainwhytheappneedspermissions.
当调用 requestPermissions() 时,系统会显示一个获取权限的提示对话框,当前应用不能配置和修改这个对话框,
如果需要提示用户一些这个权限相关的信息或说明,需要在调用 requestPermissions() 之前处理。
shouldShowRequestPermissionRationale() 的一些说明:
Tohelpfindthesituationswhereyouneedtoprovideextraexplanation,thesystemprovidestheshouldShowRequestPermissionRationale() method.
Thismethodreturns true iftheapphasrequestedthispermissionpreviouslyandtheuserdeniedtherequest.
Thatindicatesthatyoushouldprobablyexplaintotheuserwhyyouneedthepermission.
Iftheuserturneddownthepermissionrequestinthepastandchosethe Don'taskagain optioninthepermissionrequestsystemdialog,thismethodreturns false.
Themethodalsoreturns false ifthedevicepolicyprohibitstheappfromhavingthatpermission.
1.第一次请求权限时,用户拒绝了,下一次:shouldShowRequestPermissionRationale() 返回true,应该显示一些为什么需要这个权限的说明
2.第二次请求权限时,用户拒绝了,并选择了“不在提醒”的选项时:shouldShowRequestPermissionRationale() 返回false
3.设备的策略禁止当前应用获取这个权限的授权:shouldShowRequestPermissionRationale() 返回false
注意:上面的:第二次请求权限时,才会有“不在提醒”的选项,如果用户一直拒绝,并没有选择“不在提醒”的选项,下次请求权限时,会继续有“不在提醒”的选项
十、shouldShowRequestPermissionRationale() 的方法说明:
GetswhetheryoushouldshowUIwithrationaleforrequestingapermission.
Youshoulddothisonlyifyoudonothavethepermissionandthecontextinwhichthepermissionisrequesteddoesnotclearlycommunicatetotheuserwhatwouldbethebenefitfromgrantingthispermission.
Forexample,ifyouwriteacameraapp,requestingthecamerapermissionwouldbeexpectedbytheuserandnorationaleforwhyitisrequestedisneeded.Ifhowever,theappneedslocationfortaggingphotosthenanon-techsavvyusermaywonderhowlocationisrelatedtotakingphotos.InthiscaseyoumaychoosetoshowUIwithrationaleofrequestingthispermission.
根据方法说明:显示权限说明:是根据你的应用中使用的权限分类来的:1.用户容易知道应用需要获取的权限:如一个拍照应用,需要摄像头的权限,是很正常,不用提示。
2.一些用户感觉困惑的一些权限:如:分享图片,还需要获取位置的权限,这个需要提示用户:为什么需要这个权限。
postedon
2016-10-0809:49
屌丝迷途
阅读(9839)
评论(1)
编辑
收藏
举报
刷新评论刷新页面返回顶部
导航
Poweredby:
博客园
Copyright©2022屌丝迷途
Poweredby.NET6onKubernetes
延伸文章資訊
- 1Fragment.ShouldShowRequestPermissionRationale(String ...
- 2ActivityCompat - Android Developers
Call shouldShowRequestPermissionRationale before calling this API to check if the system recommen...
- 3Android 6.0权限全面详细分析和解决方案- 屌丝迷途 - 博客园
所以为了应对这种情况,系统提供了一个shouldShowRequestPermissionRationale()函数,这个函数的作用是帮助开发者找到需要向用户额外解释权限的情况, ...
- 4Android中使用shouldShowRequestPermissionRationale判斷 ...
- 5Android M Permissions : Confused on the usage of ...
It talks about the shouldShowRequestPermissionRationale() function which returns true if the app ...