博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android 运行时权限管理最佳实践
阅读量:6953 次
发布时间:2019-06-27

本文共 8051 字,大约阅读时间需要 26 分钟。

欢迎访问我的个人博客

从 Android 6.0(API 级别 23)开始,用户开始在应用运行时向其授予权限,而不是在应用安装时授予。此方法可以简化应用安装过程,因为用户在安装或更新应用时不需要授予权限。它还让用户可以对应用的功能进行更多控制;例如,用户可以选择为相机应用提供相机访问权限,而不提供设备位置的访问权限。用户可以随时进入应用的“Settings”屏幕调用权限。

正常权限和危险权限

系统权限分为几个保护级别。需要了解的两个最重要保护级别是正常权限和危险权限,如果应用声明其需要正常权限,系统会自动向应用授予该权限,如:访问网络。如果应用声明其需要危险权限,则用户必须明确向应用授予该权限,如:访问联系人、读写权限。

正常权限

官网可查

ACCESS_LOCATION_EXTRA_COMMANDSACCESS_NETWORK_STATEACCESS_NOTIFICATION_POLICYACCESS_WIFI_STATEBLUETOOTHBLUETOOTH_ADMINbaidu_pushBROADCAST_STICKYCHANGE_NETWORK_STATECHANGE_WIFI_MULTICAST_STATECHANGE_WIFI_STATEDISABLE_KEYGUARDEXPAND_STATUS_BARGET_PACKAGE_SIZEINSTALL_SHORTCUTINTERNETKILL_BACKGROUND_PROCESSESMODIFY_AUDIO_SETTINGSNFCREAD_SYNC_SETTINGSREAD_SYNC_STATSRECEIVE_BOOT_COMPLETEDREORDER_TASKSREQUEST_IGNORE_BATTERY_OPTIMIZATIONSREQUEST_INSTALL_PACKAGESSET_ALARMSET_TIME_ZONESET_WALLPAPERSET_WALLPAPER_HINTSTRANSMIT_IRUNINSTALL_SHORTCUTUSE_FINGERPRINTVIBRATEWAKE_LOCKWRITE_SYNC_SETTINGS

危险权限

可通过adb 命令获取 adb shell pm list permissions -d -g

group:android.permission-group.RCS_PERMISSIONgroup:com.google.android.gms.permission.CAR_INFORMATION  permission:com.google.android.gms.permission.CAR_VENDOR_EXTENSION  permission:com.google.android.gms.permission.CAR_MILEAGE  permission:com.google.android.gms.permission.CAR_FUELgroup:android.permission-group.CONTACTS  permission:android.permission.WRITE_CONTACTS  permission:android.permission.GET_ACCOUNTS  permission:android.permission.READ_CONTACTSgroup:android.permission-group.PHONE  permission:android.permission.READ_CALL_LOG  permission:android.permission.ANSWER_PHONE_CALLS  permission:android.permission.READ_PHONE_NUMBERS  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_VOICEMAILgroup:android.permission-group.CALENDAR  permission:android.permission.READ_CALENDAR  permission:android.permission.WRITE_CALENDARgroup:android.permission-group.CAMERA  permission:android.permission.CAMERAgroup:android.permission-group.SENSORS  permission:android.permission.BODY_SENSORSgroup:android.permission-group.LOCATION  permission:android.permission.ACCESS_FINE_LOCATION  permission:com.google.android.gms.permission.CAR_SPEED  permission:android.permission.ACCESS_COARSE_LOCATIONgroup:android.permission-group.STORAGE  permission:android.permission.READ_EXTERNAL_STORAGE  permission:android.permission.WRITE_EXTERNAL_STORAGEgroup:com.sina.weibo.permission-group  permission:com.sina.weibo.permission.USERgroup:android.permission-group.MICROPHONE  permission:android.permission.RECORD_AUDIOgroup: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

从上面的权限列表中可以看出危险权限都是分组的,如果应用请求其清单中列出的危险权限,而应用在同一权限组中已有另一项危险权限,则系统会立即授予该权限,而无需与用户进行任何交互。例如,如果某应用已经请求并且被授予了 READ_CONTACTS 权限,然后它又请求 WRITE_CONTACTS,系统将立即授予该权限。

请求权限

这里以申请日历读写权限为例

1.在 AndroidMainifest 中声明所需权限

2.检查权限

private fun checkPermissions(): Boolean {        return when (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_CALENDAR)) {            PackageManager.PERMISSION_GRANTED -> {//有此权限                true            }            PackageManager.PERMISSION_DENIED -> {//无此权限                false            }            else -> false        }    }

这里用到系统提供的 ContextCompat.checkSelfPermission 方法,用于检测某个权限是否已经被授予,方法返回值为 PackageManager.PERMISSION_GRANTED 表示已权限,为PackageManager.PERMISSION_DENIED 表示无此权限需要进行申请授权。

<span id = "requestPermission"></span>

3.申请权限

private fun requestPermissions() {        if (ActivityCompat.shouldShowRequestPermissionRationale(this,                Manifest.permission.READ_CALENDAR)) {            //  是否需要向用户解释为何申请权限              toast(this,"需要此权限管理日历")        } else {            ActivityCompat.requestPermissions(this,                    arrayOf(Manifest.permission.READ_CALENDAR),                    1)        }    }

ActivityCompat.shouldShowRequestPermissionRationale 方法 用于在实际显示权限对话框之前是否显示一个对正在请求权限的解释,在app第一次安装的时候。这个方法会返回false,因此你可以直接请求任何需要的权限。

如果用户以前拒绝了一个请求,则分为两种情况:

  • 如果用户仅拒绝没有点不再提示,这个方法将返回 true
  • 如果用户拒绝并点击不再提示,这个方法将返回 false

ActivityCompat.requestPermissions 方法 用于申请权限,第二个参数为 所需权限数组,也就是可申请一个,或多个权限。第三个参数为 requestCode 回调的时候使用

4.处理权限申请回调

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array
, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) when (requestCode) { 1 -> { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { toast(this, "已授权") } else { toast(this, "未授权") } } else -> { } } }

处理权限要实现 onRequestPermissionsResult 方法,该方法有三个参数

  • requestCode 和申请权限时 requestCode 对应,
  • permissions 申请的权限数组
  • grantResults 申请结果

完整的代码如下:

fun click(view: View?) {        when (view?.id) {            R.id.bt_query_permissions -> when (checkPermissions()) {                true -> toast(this, "有权限")                false -> toast(this, "无权限")            }            R.id.bt_request_permissions -> requestPermissions()        }    }    private fun checkPermissions(): Boolean {        return when (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_CALENDAR)) {            PackageManager.PERMISSION_GRANTED -> {//有此权限                true            }            PackageManager.PERMISSION_DENIED -> {//无此权限                false            }            else -> false        }    }    private fun requestPermissions() {        if (ActivityCompat.shouldShowRequestPermissionRationale(this,                Manifest.permission.READ_CALENDAR)) {            toast(this,"需要此权限管理日历")        } else {            ActivityCompat.requestPermissions(this,                    arrayOf(Manifest.permission.READ_CALENDAR),                    1)        }    }    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array
, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) when (requestCode) { 1 -> { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { toast(this, "已授权") } else { toast(this, "未授权") } } else -> { } } }

Android 运行时权限管理最佳实践Android 运行时权限管理最佳实践

推荐使用 RxPermissions

RxPermissions 是一个基于 RxJava 实现的权限框架,比使用 Android 自带的 API 方便很多,可扩展性高。

引入

这里以 Rxjava2 为例

repositories {    jcenter() // If not already there}dependencies {    //compile 'com.tbruyelle.rxpermissions:rxpermissions:0.9.4@aar'  // Rxjava1     compile 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.4@aar' // Rxjava2     compile "io.reactivex.rxjava2:rxjava:2.1.7"    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'}

请求权限

request 可申请一个或多个权限 直接返回是否授权成功

val rxPermissions = RxPermissions(this) rxPermissions.request(Manifest.permission.READ_CALENDAR)                .subscribe({ t ->                     if (t) {                         toast(this, "已授权")                     } else {                         toast(this, "未授权")                     }                 })

requestEach or ensureEach 来分别获取每一个权限请求的结果

rxPermissions.requestEach(Manifest.permission.READ_CALENDAR, Manifest.permission.CAMERA)                .subscribe({ t ->                    when {                        t.granted -> toast(this, "${t.name} 已授权")                        t.shouldShowRequestPermissionRationale -> toast(this, "${t.name} 未授权")                        else -> toast(this, "${t.name} 已拒绝,并不提示")                    }                })

这里的 shouldShowRequestPermissionRationale 参照上文

Android 运行时权限管理最佳实践

最后

权限申请的坑还有很多,特别是在国产手机上有各种各样的bug,这个就要具体踩坑,具体解决了

转载于:https://blog.51cto.com/13562787/2057636

你可能感兴趣的文章
bootstrap学习(二)页面
查看>>
C++ sizeof操作符的用法和strlen函数的区别
查看>>
文件的续写
查看>>
每天一道算法题(16)——翻转链表
查看>>
点亮LCD1602
查看>>
Windows下SVN备份脚本
查看>>
如何在页面中获取到ModelAndView绑定的值
查看>>
Linux 系统磁盘满处理方法
查看>>
点击按钮弹出窗口
查看>>
以Python为基础的REST(JSON为交换数据)接口的测试框架设计(一)
查看>>
MySQL中是索引
查看>>
Have Fun with Numbers及循环链表(约瑟夫问题)
查看>>
acm常用术语
查看>>
YUV格式&像素
查看>>
Asp.Net Core 快速邮件队列设计与实现
查看>>
归并排序板子
查看>>
oralce入门学习
查看>>
编程开发之--java多线程学习总结(4)
查看>>
字符串匹配
查看>>
mysql搭建及数据迁移教程
查看>>