Android M 动态权限获取

来源:互联网 发布:简单sql语句创建数据库 编辑:程序博客网 时间:2024/06/06 02:54

新的权限获取方式除了要求像之前版本一样在AndroidManifest文件中静态申请之外,应用还需根据需要请求权限,方式采用向用户显示一个请求权限的对话框。这些被动态申请的权限可以在系统设置中被手动关闭。另外,对于类别为NORMAL的权限,仍然只需要在AndroidManifest文件中静态申请,系统安装时会直接获取,对于NORMAL权限下文有详细的说明。

permission-normal

PROTECTION_NORMAL类权限

当用户安装或更新应用时,系统将授予应用所请求的属于 PROTECTION_NORMAL 的所有权限(安装时授权的一类基本权限)。这类权限包括:

android.permission.ACCESS_LOCATION_EXTRA_COMMANDSandroid.permission.ACCESS_NETWORK_STATEandroid.permission.ACCESS_NOTIFICATION_POLICYandroid.permission.ACCESS_WIFI_STATEandroid.permission.ACCESS_WIMAX_STATEandroid.permission.BLUETOOTHandroid.permission.BLUETOOTH_ADMINandroid.permission.BROADCAST_STICKYandroid.permission.CHANGE_NETWORK_STATEandroid.permission.CHANGE_WIFI_MULTICAST_STATEandroid.permission.CHANGE_WIFI_STATEandroid.permission.CHANGE_WIMAX_STATEandroid.permission.DISABLE_KEYGUARDandroid.permission.EXPAND_STATUS_BARandroid.permission.FLASHLIGHTandroid.permission.GET_ACCOUNTSandroid.permission.GET_PACKAGE_SIZEandroid.permission.INTERNETandroid.permission.KILL_BACKGROUND_PROCESSESandroid.permission.MODIFY_AUDIO_SETTINGSandroid.permission.NFCandroid.permission.READ_SYNC_SETTINGSandroid.permission.READ_SYNC_STATSandroid.permission.RECEIVE_BOOT_COMPLETEDandroid.permission.REORDER_TASKSandroid.permission.REQUEST_INSTALL_PACKAGESandroid.permission.SET_TIME_ZONEandroid.permission.SET_WALLPAPERandroid.permission.SET_WALLPAPER_HINTSandroid.permission.SUBSCRIBED_FEEDS_READandroid.permission.TRANSMIT_IRandroid.permission.USE_FINGERPRINTandroid.permission.VIBRATEandroid.permission.WAKE_LOCKandroid.permission.WRITE_SYNC_SETTINGScom.android.alarm.permission.SET_ALARMcom.android.launcher.permission.INSTALL_SHORTCUTcom.android.launcher.permission.UNINSTALL_SHORTCUT

只需要在AndroidManifest.xml中简单声明这些权限就好,安装时就授权。不需要每次使用时都检查权限,而且用户不能取消以上授权。


权限组

新的权限模型中还提出了一个权限组的概念,可以简单理解为如果一个权限组内的某个权限被获取了,那么这个组中剩余的权限也会被自动获取。例如:Android.permission-group.CALENDAR中的android.permission.WRITE_CALENDAR 权限被获取,那么应用会自动获取android.permission.READ_CALENDAR权限。

permission-group

一般动态获取方法

tip1

判定是否有权限:checkSelfPermission()

tip2

如果没有权限,弹出dialog给用户选择:requestPermission(),第二个参数code与onRequestPermissionResult()方法中的code对应

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {  
  2.     requestPermissions(  
  3.             new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },  
  4.             REQUEST_CODE_ASK_PERMISSON);  
  5. }  

tip3

判断用户是否确认了权限onRequestPermissionResult ()

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public void onRequestPermissionsResult(int requestCode,  
  2.         String permissions[], int[] grantResults) {  
  3.     switch (requestCode) {  
  4.     case REQUEST_CODE_ASK_PERMISSON:  
  5.         if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {  
  6.             // Permission Granted  
  7.         } else {  
  8.             // Permission Denied  
  9.         }  
  10.         break;  
  11.     default:  
  12.         super.onRequestPermissionsResult(requestCode, permissions,  
  13.                 grantResults);  
  14.     }  
  15. }  

tip4

在弹出权限选择的对话框前给用户show一个dialog,用于引导用户进行选择。shouldShowRequestPermissionRationale()

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {  
  2.     if (!shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {  
  3.         // Explain to the user why we need to read the contacts  
  4.         showMessage("请允许应用对SD卡进行读写操作",  
  5.                 new DialogInterface.OnClickListener() {  
  6.                     @Override  
  7.                     public void onClick(DialogInterface dialog,  
  8.                             int which) {  
  9.                         requestPermissions(  
  10.                                 new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },  
  11.                                 REQUEST_CODE_ASK_PERMISSIONS);  
  12.                     }  
  13.                 });  
  14.         return;  
  15.     }  
  16.     requestPermissions(  
  17.             new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },  
  18.             REQUEST_CODE_ASK_PERMISSIONS);  
  19. }  
  20.   
  21. private void showMessage(String message,  
  22.         DialogInterface.OnClickListener okListener) {  
  23.     new AlertDialog.Builder(MainActivity.this).setMessage(message)  
  24.             .setPositiveButton("OK", okListener).create().show();  
  25. }  

permission-ask-1

permission-ask-2

tip5

如果用户在选择权限对话框拒绝了某个权限的申请,那么再次申请该权限时会多出一个“不再询问”的checkbox,如果勾选,那么即便程序再调用requestPermission(),对话框也不会再弹出了。

permission-deny-ever

tip6

一次性处理多个权限申请

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. private void requestPermissions() {  
  2.     List<String> permissionsNeeded = new ArrayList<String>();  
  3.   
  4.     final List<String> permissionsList = new ArrayList<String>();  
  5.     if (!addPermission(permissionsList, Manifest.permission.READ_PHONE_STATE))  
  6.         permissionsNeeded.add("Phone State");  
  7.     if (!addPermission(permissionsList, Manifest.permission.WRITE_EXTERNAL_STORAGE))  
  8.         permissionsNeeded.add("Write SDcard");  
  9.     if (!addPermission(permissionsList, Manifest.permission.WRITE_CONTACTS))  
  10.         permissionsNeeded.add("Write Contacts");  
  11.   
  12.     if (permissionsList.size() > 0) {  
  13.         if (permissionsNeeded.size() > 0) {  
  14.             // Need Rationale  
  15.             String message = "You need to grant access to " + permissionsNeeded.get(0);  
  16.             for (int i = 1; i < permissionsNeeded.size(); i++)  
  17.                 message = message + ", " + permissionsNeeded.get(i);  
  18.             showMessageOKCancel(message,  
  19.                     new DialogInterface.OnClickListener() {  
  20.                         @Override  
  21.                         public void onClick(DialogInterface dialog, int which) {  
  22.                             requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),  
  23.                                     REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);  
  24.                         }  
  25.                     });  
  26.             return;  
  27.         }  
  28.         requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),  
  29.                 REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);  
  30.         return;  
  31.     }  
  32. }  
  33. private boolean addPermission(List<String> permissionsList, String permission) {  
  34.     if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {  
  35.         permissionsList.add(permission);  
  36.         if (!shouldShowRequestPermissionRationale(permission)){  
  37.             return false;  
  38.         }  
  39.     }  
  40.     return true;  
  41. }  


如果所有权限被授权,依然回调onRequestPermissionsResult,我用hashmap让代码整洁便于阅读。

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {  
  3.     switch (requestCode) {  
  4.         case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS:  
  5.             {  
  6.             Map<String, Integer> perms = new HashMap<String, Integer>();  
  7.             // Initial  
  8.             perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED);  
  9.             perms.put(Manifest.permission.READ_CONTACTS, PackageManager.PERMISSION_GRANTED);  
  10.             perms.put(Manifest.permission.WRITE_CONTACTS, PackageManager.PERMISSION_GRANTED);  
  11.             // Fill with results  
  12.             for (int i = 0; i < permissions.length; i++)  
  13.                 perms.put(permissions[i], grantResults[i]);  
  14.             // Check for ACCESS_FINE_LOCATION  
  15.             if (perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED  
  16.                     && perms.get(Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED  
  17.                     && perms.get(Manifest.permission.WRITE_CONTACTS) == PackageManager.PERMISSION_GRANTED) {  
  18.                 // All Permissions Granted  
  19.                 insertDummyContact();  
  20.             } else {  
  21.                 // Permission Denied  
  22.                 Toast.makeText(MainActivity.this"Some Permission is Denied", Toast.LENGTH_SHORT)  
  23.                         .show();  
  24.             }  
  25.             }  
  26.             break;  
  27.         default:  
  28.             super.onRequestPermissionsResult(requestCode, permissions, grantResults);  
  29.     }  
  30. }  
有的情况,一个权限没有授权,就不可用;但是也有情况,能工作,但是表现的是有所限制的。对于这个我不做评价,你自己设计吧。

用兼容库使代码兼容旧版

以上代码在android 6.0以上运行没问题,但是23 api之前就不行了,因为没有那些方法。
粗暴的方法是检查版本

if (Build.VERSION.SDK_INT >= 23) {    // Marshmallow+} else {    // Pre-Marshmallow}

但是太复杂,我建议用v4兼容库,已对这个做过兼容,用这个方法代替:

  • ContextCompat.checkSelfPermission()
    被授权函数返回PERMISSION_GRANTED,否则返回PERMISSION_DENIED ,在所有版本都是如此。
  • ActivityCompat.requestPermissions()
    这个方法在M之前版本调用,OnRequestPermissionsResultCallback 直接被调用,带着正确的 PERMISSION_GRANTED或者 PERMISSION_DENIED 。
  • ActivityCompat.shouldShowRequestPermissionRationale()
    在M之前版本调用,永远返回false。
    用v4包的这三方法,完美兼容所有版本!这个方法需要额外的参数,Context or Activity。别的就没啥特别的了。下面是代码:

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. private void insertDummyContactWrapper() {  
  2.     int hasWriteContactsPermission = ContextCompat.checkSelfPermission(MainActivity.this,  
  3.             Manifest.permission.WRITE_CONTACTS);  
  4.     if (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {  
  5.         if (!ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,  
  6.                 Manifest.permission.WRITE_CONTACTS)) {  
  7.             showMessageOKCancel("You need to allow access to Contacts",  
  8.                     new DialogInterface.OnClickListener() {  
  9.                         @Override  
  10.                         public void onClick(DialogInterface dialog, int which) {  
  11.                             ActivityCompat.requestPermissions(MainActivity.this,  
  12.                                     new String[] {Manifest.permission.WRITE_CONTACTS},  
  13.                                     REQUEST_CODE_ASK_PERMISSIONS);  
  14.                         }  
  15.                     });  
  16.             return;  
  17.         }  
  18.         ActivityCompat.requestPermissions(MainActivity.this,  
  19.                 new String[] {Manifest.permission.WRITE_CONTACTS},  
  20.                 REQUEST_CODE_ASK_PERMISSIONS);  
  21.         return;  
  22.     }  
  23.     insertDummyContact();  
  24. }  

后两个方法,我们也可以在Fragment中使用,用v13兼容包:FragmentCompat.requestPermissions() and FragmentCompat.shouldShowRequestPermissionRationale().和activity效果一样。

第三方库简化代码

以上代码真尼玛复杂。为解决这事,有许多第三方库已经问世了,真屌真有速度。我试了很多最终找到了个满意的hotchemi's PermissionsDispatcher。
他和我上面做的一样,只是简化了代码。灵活易扩展,试一下吧。如果不满足你可以找些其他的。

如果我的app还开着呢,权限被撤销了,会发生生么

权限随时可以被撤销。


Paste_Image.png

当app开着的时候被撤消了会发生什么呢?我试过了发现这时app会突然终止 terminated。app中的一切都被简单粗暴的停止了,因为terminated!对我来说这可以理解,因为系统如果允许它继续运行(没有某权限),这会召唤弗雷迪到我的噩梦里。或许更糟...

结论建议

我相信你对新权限模型已经有了清晰的认识。我相信你也意识到了问题的严峻。
但是你没得选择。新运行时权限已经在棉花糖中被使用了。我们没有退路。我们现在唯一能做的就是保证app适配新权限模型.
欣慰的是只有少数权限需要运行时权限模型。大多数常用的权限,例如,网络访问,属于Normal Permission 在安装时自动会授权,当然你要声明,以后无需检查。因此,只有少部分代码你需要修改。
两个建议:
1.严肃对待新权限模型
2.如果你代码没支持新权限,不要设置targetSdkVersion 23 。尤其是当你在Studio新建工程时,不要忘了修改!

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 5s触摸不灵敏怎么办 导航触屏失灵怎么办 汽车导航触摸屏失灵怎么办 爱丽舍导航仪触屏失灵怎么办 手机要开机很久怎么办 小米手机屏失灵怎么办 5s手机屏幕松动怎么办 手机按钮不灵了怎么办 手机触摸屏不灵了怎么办 mac触摸板失灵怎么办 苹果屏幕触摸失灵怎么办 三星手机屏幕没反应怎么办 手机开机定屏怎么办 手机触摸局部失灵怎么办 苹果手机屏幕按键失灵怎么办 5s锁屏键坏了怎么办 平板版本太低怎么办 手机屏局部失灵怎么办 iphone8触屏不灵敏怎么办 苹果机8屏幕失灵怎么办 车钥匙丢车上怎么办 指纹锁华盖坏了怎么办 非法入了户口怎么办 司考成绩单丢了怎么办 小饭桌转让手续怎么办 两个领导不和你怎么办 两个领导意见不一致怎么办 两个领导对立我怎么办 投诉申通没用怎么办 领导作风有问题怎么办 做完火疗受风了难受怎么办 鼻子做的不好看怎么办 埋线双眼皮出血怎么办 割完双眼皮出血怎么办 全切双眼皮出血怎么办 割双眼皮出血了怎么办 割双眼皮后出血怎么办 双眼皮手术后出血怎么办 缝双眼皮开了怎么办 朋友网没了怎么办 压疮发生后怎么办