安卓6.0权限申请详解

安卓6.0的一大变化就是对于权限的限制,首次安装应用时会产生一个权限请求列表,需要用户手动逐个确认每个权限,应用才能获取该权限。而在6.0之前默认开启的,因此会产生一些应用会读取用户的一些隐私信息,影响用户体验。本文根据实际项目开发经验,简述基于安卓6.0开发的动态权限调用相关的API使用,方便开发者快速调用。

一、安卓6.0权限相关介绍

Google将权限分为两类,一类是Normal Permissions,该类权限不涉及用户隐私,不需要用户进行授权,比如蓝牙、访问网络等;另一类是Dangerous Permission,一般是涉及用户隐私,需要用户手动授权,比如摄像机、获取手机状态和读写SD卡等。

  • Normal Permissions:
ACCESS_LOCATION_EXTRA_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMIN
BROADCAST_STICKY
CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE
CHANGE_WIFI_STATE
DISABLE_KEYGUARD
EXPAND_STATUS_BAR
GET_PACKAGE_SIZE
INSTALL_SHORTCUT
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_INSTALL_PACKAGES
SET_ALARM
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
UNINSTALL_SHORTCUT
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS
  • Dangerous Permissions:
group:android.permission-group.CONTACTS
  permission:android.permission.WRITE_CONTACTS
  permission:android.permission.GET_ACCOUNTS
  permission:android.permission.READ_CONTACTS

group:android.permission-group.PHONE
  permission:android.permission.READ_CALL_LOG
  permission:android.permission.READ_PHONE_STATE
  permission:android.permission.CALL_PHONE
  permission:android.permission.WRITE_CALL_LOG
  permission:android.permission.USE_SIP
  permission:android.permission.PROCESS_OUTGOING_CALLS
  permission:com.android.voicemail.permission.ADD_VOICEMAIL

group:android.permission-group.CALENDAR
  permission:android.permission.READ_CALENDAR
  permission:android.permission.WRITE_CALENDAR

group:android.permission-group.CAMERA
  permission:android.permission.CAMERA

group:android.permission-group.SENSORS
  permission:android.permission.BODY_SENSORS

group:android.permission-group.LOCATION
  permission:android.permission.ACCESS_FINE_LOCATION
  permission:android.permission.ACCESS_COARSE_LOCATION

group:android.permission-group.STORAGE
  permission:android.permission.READ_EXTERNAL_STORAGE
  permission:android.permission.WRITE_EXTERNAL_STORAGE

group:android.permission-group.MICROPHONE
  permission:android.permission.RECORD_AUDIO

group:android.permission-group.SMS
  permission:android.permission.READ_SMS
  permission:android.permission.RECEIVE_WAP_PUSH
  permission:android.permission.RECEIVE_MMS
  permission:android.permission.RECEIVE_SMS
  permission:android.permission.SEND_SMS
  permission:android.permission.READ_CELL_BROADCASTS

可以通过命令行查看手机的Dangerous Permissions权限列表。

adb shell pm list permissions -d -g   

细心的读者会发现Dangerous Permissions的权限都是一组一组的,确实,Google就是这样设计的,但是对于分组机制也会给我的应用带你一定的风险,因为6.0及以上机器对于危险授权的机制是这样的:假如应用已被用户授权了同一组的某个危险权限,那么系统会立即授权同组的其他权限,而不需用户手动授权。例如你的应用对WRITE_CONTACTS授权了,那么对于CONTACTS该组内的其他权限也是授权的。在弹出权限授权对话框的授权请求,也是指对该组权限的授权申请。

二、安卓6.0动态申请权限实现

安卓6.0(23)动态权限的申请包括:AndroidManifest添加、检查权限、申请授权和权限处理回调四个过程。

  • AndroidManifest添加
    在AndroidManifest添加相关请求的权限,该操作与之前的权限添加类似,这里要声明一下:即使动态申请权限,也需要在此处添加相关的权限,否则无法动态申请。因为代码逻辑处理的权限申请的检查以及申请回调。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.XXX"/>   //XXX:需要申请的权限名称
  • 检查权限
    检查权限的逻辑处理一般是在应用启动的Activity的onCreate()中实现。
 ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED

ContextCompat.checkSelfPermissionf()方法是检查某个权限是否已经被授权,第一个参数为我们当前的Activity的引用,第二个参数为我们申请的权限,该方法的返回值为:
PackageManager.PERMISSION_DENIED:拒绝
PackageManager.PERMISSION_GRANTED:授权
一般我们都是判断其返回值是否为GRANTED,如不是则判断未授权,申请授权;否则为已经授权。

  • 申请授权
 MainActivity.this.requestPermissions(permissions, requestCode);

其中permissions为申请权限的字符串数组;requestCode用于回调的监测。在申请授权时我们可以一次请求申请多个权限。
一般使用时,权限的请求和申请授权的操作是一起处理的。监测我们需要申请的权限是否已经授权,如果授权,则调用授权后的逻辑处理;否则,调用权限申请的逻辑。如果之前已经开启并授权成功,那么不会显示权限请求的对话框,逻辑进入“已经授权”的处理中。简单的逻辑处理如下。

String[] permissions = {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE};
        if (Build.VERSION.SDK_INT >= 23) {
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                //申请摄像头权限
                if (Manifest.permission.CAMERA.equals(Manifest.permission.CAMERA)) {
                    Log.i(TAG, "requestPermission: 请求摄像头权限");
                    this.requestPermissions(permissions, CAMERA_CODE);
                }
            } else {
                Log.i(TAG, "requestPermission:已经申请了摄像头");
            }

            //申请读写权限
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                if (Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                    Log.i(TAG, "requestPermission: 请求外部读写权限");
                    this.requestPermissions(permissions, WRITE_EXTERNAL_STORAGE_CODE);
                }
            } else {
                Log.i(TAG, "requestPermission: 已经申请了外部读写权限");
            }
        }
  • 权限处理回调
@Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        Log.i(TAG, "onRequestPermissionsResult: " + grantResults.length);
        switch (requestCode) {
            case CAMERA_CODE:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Log.i(TAG, "onRequestPermissionsResult: 摄像头权限请求成功");
                } else {
                    Log.i(TAG, "onRequestPermissionsResult: 摄像头权限请求失败");
                }
                break;
            case WRITE_EXTERNAL_STORAGE_CODE:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Log.i(TAG, "onRequestPermissionsResult: 读写权限请求成功");
                } else {
                    Log.i(TAG, "onRequestPermissionsResult: 读写权限请求失败");
                }
                break;
            default:
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

权限请求的回调,只需重写onRequestPermissionsResult()方法,根据我们在权限请求的requestCode作为区分不同权限请求的回调判断。例如本例中针对CAMERA_CODE和WRITE_EXTERNAL_STORAGE_CODE分别处理。grantResults对于申请的请求结果,在请求数组包含几个请求字符串,这里的回调数字就是多少。通过判断grantResults[i]对应的结果是否为PackageManager.PERMISSION_GRANTED来判决权限请求是否成功,如果我们在应用请求时,选择拒绝,则此处回调显示请求失败。
由于此处包含授权成功的回调,因此我们在应用开发时,要注意在应用回调成功和失败时的分别逻辑处理。
至此,安卓6.0动态请求权限的逻辑处理讲解结束,总结一下:
1、AndroidManifest添加:该添加过程与此前的权限添加一样,根据我们需要添加相应权限(注意:动态申请的也要添加);
2、检查权限和申请授权:监测SDK是否大于23,如大于则动态申请权限;包括两个重要方法监测权限(ContextCompat.checkSelfPermission())和请求权限(MainActivity.this.requestPermissions())。如果监测已经请求了权限,则直接处理相应逻辑;
3、权限处理回调:根据requestCode判断相应的权限请求回调,根据grantResults[i]判断权限请求结果,然后做相应的逻辑处理。

除了我们自己编写外,我们还可以借助网上一些开源框架,例如PermissionGen,我们可直接调用即可,详情可以在GitHub上看相关的文档使用。

猜你喜欢

转载自blog.csdn.net/xk7298/article/details/79614542