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权限也是安装时授权。

Android 运行时权限_第1张图片
动态权限流程图
Android 运行时权限_第2张图片
‘不再提示’流程图

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 运行时权限_第3张图片

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

兼容问题

一. 动态权限检查需在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 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 变更

你可能感兴趣的:(Android 运行时权限)