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

PROTECTION_NORMAL类权限

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

android.permission.ACCESS_LOCATION_EXTRA_COMMANDS
android.permission.ACCESS_NETWORK_STATE
android.permission.ACCESS_NOTIFICATION_POLICY
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.CHANGE_WIMAX_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.READ_SYNC_SETTINGS
android.permission.READ_SYNC_STATS
android.permission.RECEIVE_BOOT_COMPLETED
android.permission.REORDER_TASKS
android.permission.REQUEST_INSTALL_PACKAGES
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.USE_FINGERPRINT
android.permission.VIBRATE
android.permission.WAKE_LOCK
android.permission.WRITE_SYNC_SETTINGS
com.android.alarm.permission.SET_ALARM
com.android.launcher.permission.INSTALL_SHORTCUT
com.android.launcher.permission.UNINSTALL_SHORTCUT

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

权限组

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

一般动态获取方法

tip1

判定是否有权限:checkSelfPermission()

tip2

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

if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {requestPermissions(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },REQUEST_CODE_ASK_PERMISSON);
}

tip3

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

public void onRequestPermissionsResult(int requestCode,String permissions[], int[] grantResults) {switch (requestCode) {case REQUEST_CODE_ASK_PERMISSON:if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {// Permission Granted} else {// Permission Denied}break;default:super.onRequestPermissionsResult(requestCode, permissions,grantResults);}
}

tip4

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

if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {if (!shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {// Explain to the user why we need to read the contactsshowMessage("请允许应用对SD卡进行读写操作",new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog,int which) {requestPermissions(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },REQUEST_CODE_ASK_PERMISSIONS);}});return;}requestPermissions(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },REQUEST_CODE_ASK_PERMISSIONS);
}private void showMessage(String message,DialogInterface.OnClickListener okListener) {new AlertDialog.Builder(MainActivity.this).setMessage(message).setPositiveButton("OK", okListener).create().show();
}

tip5

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

tip6

一次性处理多个权限申请

private void requestPermissions() {List<String> permissionsNeeded = new ArrayList<String>();final List<String> permissionsList = new ArrayList<String>();if (!addPermission(permissionsList, Manifest.permission.READ_PHONE_STATE))permissionsNeeded.add("Phone State");if (!addPermission(permissionsList, Manifest.permission.WRITE_EXTERNAL_STORAGE))permissionsNeeded.add("Write SDcard");if (!addPermission(permissionsList, Manifest.permission.WRITE_CONTACTS))permissionsNeeded.add("Write Contacts");if (permissionsList.size() > 0) {if (permissionsNeeded.size() > 0) {// Need RationaleString message = "You need to grant access to " + permissionsNeeded.get(0);for (int i = 1; i < permissionsNeeded.size(); i++)message = message + ", " + permissionsNeeded.get(i);showMessageOKCancel(message,new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);}});return;}requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);return;}
}
private boolean addPermission(List<String> permissionsList, String permission) {if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {permissionsList.add(permission);if (!shouldShowRequestPermissionRationale(permission)){return false;}}return true;
}

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

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {switch (requestCode) {case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS:{Map<String, Integer> perms = new HashMap<String, Integer>();// Initialperms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED);perms.put(Manifest.permission.READ_CONTACTS, PackageManager.PERMISSION_GRANTED);perms.put(Manifest.permission.WRITE_CONTACTS, PackageManager.PERMISSION_GRANTED);// Fill with resultsfor (int i = 0; i < permissions.length; i++)perms.put(permissions[i], grantResults[i]);// Check for ACCESS_FINE_LOCATIONif (perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED&& perms.get(Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED&& perms.get(Manifest.permission.WRITE_CONTACTS) == PackageManager.PERMISSION_GRANTED) {// All Permissions GrantedinsertDummyContact();} else {// Permission DeniedToast.makeText(MainActivity.this, "Some Permission is Denied", Toast.LENGTH_SHORT).show();}}break;default:super.onRequestPermissionsResult(requestCode, permissions, grantResults);}
}

有的情况,一个权限没有授权,就不可用;但是也有情况,能工作,但是表现的是有所限制的。对于这个我不做评价,你自己设计吧。

用兼容库使代码兼容旧版

以上代码在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。别的就没啥特别的了。下面是代码:
private void insertDummyContactWrapper() {int hasWriteContactsPermission = ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.WRITE_CONTACTS);if (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {if (!ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,Manifest.permission.WRITE_CONTACTS)) {showMessageOKCancel("You need to allow access to Contacts",new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {ActivityCompat.requestPermissions(MainActivity.this,new String[] {Manifest.permission.WRITE_CONTACTS},REQUEST_CODE_ASK_PERMISSIONS);}});return;}ActivityCompat.requestPermissions(MainActivity.this,new String[] {Manifest.permission.WRITE_CONTACTS},REQUEST_CODE_ASK_PERMISSIONS);return;}insertDummyContact();
}

后两个方法,我们也可以在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新建工程时,不要忘了修改!


参考:http://www.jianshu.com/p/e1ab1a179fbb

Android M 动态权限获取相关推荐

  1. Android开发之 permission动态权限获取

    权限查看:戳这里 在文章最后附上DEMO DEMO效果: 一.说明: 1.支持单个权限.多个权限.单个权限组.多个权限组请求 2.不指定权限则自动获取清单文件上的危险权限进行申请 3.如果动态申请的权 ...

  2. android悬浮动态权限,android应用内悬浮窗-自动贴边,不需要权限!

    简单基于注释的API来处理运行时6.0权限配置 热门度(没变化) 10.0/10 (没变化) ">10.0 活跃度(没变化) 0.8/10 (没变化) ">0.8 Wa ...

  3. android悬浮动态权限,Android 获取判断是否有悬浮窗权限的方法

    现在很多应用都会用到悬浮窗,很多国产rom把悬浮窗权限加入控制了,你就需要判断是否有悬浮窗权限,然后做对应操作. Android 原生有自带权限管理的,只是被隐藏了.看android源码在androi ...

  4. android如何获得开发者权限,Android 动态权限获取 超级简单的方式

    1.添加依赖 implementation 'com.werb.permissionschecker:permissionschecker:0.0.1-beta2' 2.声明 写你想要获取的权限 pr ...

  5. [Android]从canDrawOverlays权限获取错误说起

    从Settings.canDrawOverlays到Binder.getCallingPid 在调试程序的时候,发现一个奇怪的问题,在主线程调用Settings.canDrawOverlays判断是否 ...

  6. android临时root权限获取失败,安卓手机为何获取Root权限失败?分析手机Root失败的原因是什么?...

    如今,手机预装的APP应用程序实在太多,对于有系统纯净强迫症的用户来说,拿到手机第一件事就是折腾,通过Root或者刷机解决问题,那么我们在获取ROOT权限的时候可能会遇到失败,那么安卓手机为何获取Ro ...

  7. android电视root权限获取,电视盒子/ 智能电视如何通过ADB获取ROOT权限?

    如何通过adb获取root权限(安卓电视盒和智能电视通用)? Android 系统rom里面最主要的就3个文件:boot.img.system.img.userdata.img 其中boot.img ...

  8. android 如何 root权限获取,如何获取android手机root权限获取

    安卓手机的卡与不卡,和是否root无关. 安卓的卡,是其开发时就注定的,其原因如下: 1.内存小/少 手机内存在手机出厂的时候就已经固定了,其总量无法调整,不能像电脑一样加内存条.所以想提升可用内存, ...

  9. android 动态权限aop,Android — AOP 动态权限申请

    初始化权限申请失败的监听 PermissionUtil.init(new PermissionUtil.PermissionGlobalConfigCallback() { //直接申请权限 @Ove ...

最新文章

  1. Java 并发编程中使用 ReentrantLock 替代 synchronized 关键字原语
  2. K折交叉验证(StratifiedKFold与KFold比较)
  3. 编程之美-程序改错方法整理
  4. Hadoop HDFS (3) JAVA訪问HDFS之二 文件分布式读写策略
  5. hdu5247 找连续数
  6. 动态卡片:富媒体内容井喷式增长下,新一代移动端动态研发的模式
  7. 用NGINX做负载均衡,keepalived高可用
  8. 灵活使用DataView
  9. selenium 服务器端运行命令
  10. mysql : 常用管理命令
  11. 分析一块某宝上的WiFi摄像头模块
  12. Groovy探索之MOP 十五 方法名的动态性(2)
  13. SAP 必须参照采购申请创建采购订单的配置方法
  14. 09-slash_backslash
  15. 智能小区 安防技术详解及安防隐患杂谈
  16. 如何初版一本书——出版社选择
  17. OCR文本检测模型—SegLink
  18. wf信号极佳但是显示连接不到服务器,wifi信号满格却上不了网怎么办【原因及解决方法】...
  19. 使用SET修改组合SAS数据集
  20. Win10+Python+virtualenv 环境配置

热门文章

  1. FFmpeg的HEVC解码器源代码简单分析
  2. python pandas clip
  3. 【工具】1063- 前端40+精选VSCode插件,总有几个你未拥有!
  4. 数据恢复如何看异或问题
  5. PPT处理福音!Aspose.Slides最新版实现自主的跨平台3D引擎
  6. 键盘按0键出仅计算机复制,键盘上复制粘贴按哪两个键
  7. NTVDM CPU 遇到无效的指令的解决方法大全
  8. android button 字母自动变大写的解决
  9. Ceph集群搭建及其运用(块存储、ceph文件系统)
  10. 【Webpack5笔记】Webpack高级配置---提升开发体验