从 Android 6.0(API 级别 23)开始,用户开始在应用运行时向其授予权限,而不是在应用安装时授予。此方法可以简化应用安装过程,因为用户在安装或更新应用时不需要授予权限。它还让用户可以对应用的功能进行更多控制;例如,用户可以选择为相机应用提供相机访问权限,而不提供设备位置的访问权限。用户可以随时进入应用的“Settings”屏幕调用权限。
正常权限和危险权限
系统权限分为几个保护级别。需要了解的两个最重要保护级别是正常权限和危险权限,如果应用声明其需要正常权限,系统会自动向应用授予该权限,如:访问网络。如果应用声明其需要危险权限,则用户必须明确向应用授予该权限,如:访问联系人、读写权限。
正常权限
官网可查 点击查询
ACCESS_LOCATION_EXTRA_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMINbaidu_push
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_IGNORE_BATTERY_OPTIMIZATIONS
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
危险权限
可通过adb 命令获取 adb shell pm list permissions -d -g
group:android.permission-group.RCS_PERMISSION
group: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_FUEL
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.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_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:com.google.android.gms.permission.CAR_SPEED
permission:android.permission.ACCESS_COARSE_LOCATION
group:android.permission-group.STORAGE
permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.WRITE_EXTERNAL_STORAGE
group:com.sina.weibo.permission-group
permission:com.sina.weibo.permission.USER
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
从上面的权限列表中可以看出危险权限都是分组的,如果应用请求其清单中列出的危险权限,而应用在同一权限组中已有另一项危险权限,则系统会立即授予该权限,而无需与用户进行任何交互。例如,如果某应用已经请求并且被授予了 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 表示无此权限需要进行申请授权。
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 -> {
}
}
}
推荐使用 RxPermissions
RxPermissions 是一个基于 RxJava 实现的权限框架,比使用 Android 自带的 API 方便很多,可扩展性高。GitHub 地址
引入
这里以 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 参照上文 权限申请
最后
权限申请的坑还有很多,特别是在国产手机上有各种各样的bug,这个就要具体踩坑,具体解决了