Android 运行时权限

  1. M 6.0
  2. O 8.0

M 6.0


Android 6.0 版本(Api 23)推出了 动态权限管理

应用权限简介

Android应用默认情况下不拥有任何权限,申请权限要在AndroidManifest.xml中静态声明。

若未在manifest中声明权限,则在调用相应功能时,将抛出异常,一般不会catch该异常,程序会直接崩溃:
Caused by: java.lang.SecurityException: Permission denied (missing INTERNET permission?)

在** Android 6.0 之前, 所有的权限都在安装应用时显示给用户,选择安装则表示授权全部权限,之后无法取消授权。
Android 6.0 **之后, 危险权限(dangerous)需在程序运行时显式弹框,请求用户授权。何时弹框由应用程序决定。

对于普通权限(normal),仍保持原声明方式,在安装应用程序时予以授权。

保护等级

permission的保护等级通过protectionLevel属性设置,共4种:

  1. normal
  2. dangerous
  3. signature
  4. signatureOrSystem

详见:http://developer.android.com/guide/topics/manifest/permission-element.html

签名相关的不常用, 剩下normaldangerous
官网 Guides: https://developer.android.com/guide/topics/security/permissions.html#normal-dangerous

总结下来:所有的权限仍在manifest中静态声明,normal权限在安装时自动授权,dangerous权限需应用明确地请求用户授权。
对于Android 6.0以下的手机,或以前开发的旧应用,dangerous权限也是安装时授权。

动态权限流程图
‘不再提示’流程图

normal 类权限

当安装或更新时,系统将授予应用请求的属于 PROTECTION_NORMAL 的所有权限。只需在AndroidManifest.xml中声明,不必每次使用都检查权限,且用户不能取消以上授权。

序号 权限
1 android.permission.ACCESS_LOCATION_EXTRA_COMMANDS
2 android.permission.ACCESS_NETWORK_STATE
3 android.permission.ACCESS_NOTIFICATION_POLICY
4 android.permission.ACCESS_WIFI_STATE
5 android.permission.ACCESS_WIMAX_STATE
6 android.permission.BLUETOOTH
7 android.permission.BLUETOOTH_ADMIN
8 android.permission.BROADCAST_STICKY
9 android.permission.CHANGE_NETWORK_STATE
10 android.permission.CHANGE_WIFI_MULTICAST_STATE
11 android.permission.CHANGE_WIFI_STATE
12 android.permission.CHANGE_WIMAX_STATE
13 android.permission.DISABLE_KEYGUARD
14 android.permission.EXPAND_STATUS_BAR
15 android.permission.FLASHLIGHT
16 android.permission.GET_ACCOUNTS
17 android.permission.GET_PACKAGE_SIZE
18 android.permission.INTERNET
19 android.permission.KILL_BACKGROUND_PROCESSES
20 android.permission.MODIFY_AUDIO_SETTINGS
21 android.permission.NFC
22 android.permission.READ_SYNC_SETTINGS
23 android.permission.READ_SYNC_STATS
24 android.permission.RECEIVE_BOOT_COMPLETED
25 android.permission.REORDER_TASKS
26 android.permission.REQUEST_INSTALL_PACKAGES
27 android.permission.SET_TIME_ZONE
28 android.permission.SET_WALLPAPER
29 android.permission.SET_WALLPAPER_HINTS
30 android.permission.SUBSCRIBED_FEEDS_READ
31 android.permission.TRANSMIT_IR
32 android.permission.USE_FINGERPRINT
33 android.permission.VIBRATE
34 android.permission.WAKE_LOCK
35 android.permission.WRITE_SYNC_SETTINGS
36 com.android.alarm.permission.SET_ALARM
37 com.android.launcher.permission.INSTALL_SHORTCUT
38 com.android.launcher.permission.UNINSTALL_SHORTCUT

权限组

新的权限模型提出了权限组的概念,即:同权限组内的某个权限被授权了,则该组中剩余的权限也会被自动获取授权。例:Android.permission-group.CALENDAR权限组中的android.permission.WRITE_CALENDAR权限被授权,则应用会自动获取android.permission.READ_CALENDAR权限。

序号 名称 权限组 权限
1 日历 android.permission-group.CALENDAR android.permission.READ_CALENDAR
android.permission.WRITE_CALENDAR
2 摄像头 android.permission-group.CAMERA android.permission.CAMERA
3 通讯录 android.permission-group.CONTACTS android.permission.READ_CONTACTS
android.permission.WRITE_CONTACTS
android.permission.GET_ACCOUNTS
4 地理位置 android.permission-group.LOCATION android.permission.ACCESS_FINE_LOCATION
android.permission.ACCESS_COARSE_LOCATION
5 麦克风 android.permission-group.MICROPHONE android.permission.RECORD_AUDIO
6 电话 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
7 传感器 android.permission-group.SENSORS android.permission.BODY_SENSORS
8 短信 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
9 存储空间 android.permission-group.STORAGE android.permission.READ_EXTERNAL_STORAGE
android.permission.WRITE_EXTERNAL_STORAGE

动态权限使用

判断是否有权限:checkSelfPermission()

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

if(ContextCompat.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {  
    ActivityCompat.requestPermissions(new String[] { Manifest.permission.CAMERA}, REQUEST_CAMERA);  
}  

弹出权限选择对话框前弹出提示,用于引导用户选择:shouldShowRequestPermissionRationale()

if(ContextCompat.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {  
    if (ActivityCompat.shouldShowRequestPermissionRationale(activity.this, Manifest.permission.CAMERA)) {    
        Snackbar.make(view, "请允许应用使用照相机", Snackbar.LENGTH_INDEFINITE)            
            .setAction(R.string.ok, new View.OnClickListener() {                
                @Override               
                public void onClick(View view) {                      
                    ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA);                
                }            
            })            
          .show();
    } else {    
        ActivityCompat.requestPermissions(new String[] { Manifest.permission.CAMERA}, REQUEST_CAMERA);  
    }
}  

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

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,                                       @NonNull int[] grantResults) { 
    switch (requestCode) {
        case REQUEST_CAMERA:
            if (PermissionUtil.verifyPermissions(grantResults)) { 
                Snackbar.make(mLayout, "同意授权", Snackbar.LENGTH_SHORT).show(); 
            } else { 
                Snackbar.make(mLayout, "拒绝授权", Snackbar.LENGTH_SHORT).show();                  
            }   
        break;  
        default: 
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);    
    }
}

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

同时处理多个权限,方案待定

兼容问题

一. 动态权限检查需在android 6.0版本以上运行,即api 23之前不能运行:

  1. 判断SDK版本号
public static boolean checkSDKVersion_23() {    
    return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
  1. 使用v4兼容库,兼容所有版本
  • ContextCompat.checkSelfPermission()
    授权返回PERMISSION_GRANTED,否则返回PERMISSION_DENIED,所有版本都是如此。

  • ActivityCompat.requestPermissions()
    M之前版本调用,OnRequestPermissionsResultCallback 直接被调用,带着正确的 PERMISSION_GRANTEDPERMISSION_DENIED

  • ActivityCompat.shouldShowRequestPermissionRationale()
    M之前版本调用,永远返回false

二. 在Fragment中使用,用v13兼容包,效果一样:

  • FragmentCompat.requestPermissions()
  • FragmentCompat.shouldShowRequestPermissionRationale()
  • Fragment中嵌套Fragment,子Fragment中建议用getParentFragment().requestPermissions方法回调父Fragment中的onRequestPermissionsResult,添加以下代码将回调透传到子Fragment
@Override 
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { 
      super.onRequestPermissionsResult(requestCode, permissions, grantResults);     
      List<Fragment> fragmentList = getChildFragmentManager().getFragments(); 
      if (fragmentList != null && fragmentList.size > 0) { 
          for (Fragment fragment : fragmentList ) { 
              if (fragment != null) { 
                  fragment.onRequestPermissionsResult(requestCode,permissions,grantResults); 
              } 
          } 
      } 
}

开源

PermissionsDispatcher 使用标注方式暂不支持嵌套Fragment
RxPermissions 基于RxJava
android-RuntimePermissions 谷歌官方示例

参考

Android M 动态权限获取

O 8.0


官方:
在 Android 8.0 之前,如果应用在运行时请求权限并且被授予该权限,系统会错误地将属于同一权限组并且在清单中注册的其他权限也一起授予应用。

对于针对 Android 8.0 的应用,此行为已被纠正。系统只会授予应用明确请求的权限。然而,一旦用户为应用授予某个权限,则所有后续对该权限组中权限的请求都将被自动批准。

例如,假设某个应用在其清单中列出 READ_EXTERNAL_STORAGE。应用请求 READ_EXTERNAL_STORAGE,并且用户授予了该权限。如果该应用针对的是 API 级别 24 或更低级别,系统还会同时授予 WRITE_EXTERNAL_STORAGE,因为该权限也属于同一 STORAGE 权限组并且也在清单中注册过。如果该应用针对的是 Android 8.0,则系统此时仅会授予 READ_EXTERNAL_STORAGE;不过,如果该应用后来又请求 WRITE_EXTERNAL_STORAGE,则系统会立即授予该权限,而不会提示用户。

8.0 变更

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 225,151评论 6 523
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,465评论 3 405
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 172,429评论 0 368
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 61,147评论 1 301
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,149评论 6 400
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,614评论 1 315
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,965评论 3 429
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,950评论 0 279
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,486评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,524评论 3 347
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,640评论 1 355
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,228评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,976评论 3 340
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,407评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,552评论 1 277
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,215评论 3 381
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,714评论 2 366

推荐阅读更多精彩内容