简介
对于权限,每个android开发者应该很熟悉了,对于targetSDK大于23的时候需要对某些敏感权限进行动态申请,比如获取通讯录权限、相机权限、定位权限等。
在android 6.0中也同时添加了权限组的概念,若用户同意组内的某一个权限,那么系统默认app可以使用组内的所有权限,无需再次申请。
这里贴一张权限组的图片:
申请权限API
先介绍一下android 6.0以上动态申请权限的流程,申请权限,用户可以点击拒绝,再次申请的时候可以选择不再提醒。
下面说介绍一下运行时申请权限需要用到的API,代码示例使用kotlin实现
- 在Manifest中注册
<uses-permission android:name="android.permission.XXX"/>
- 检查用户是否同意了某个权限
// (API) int checkSelfPermission (Context context, String permission)
ContextCompat.checkSelfPermission(context, Manifest.permission.XXX) != PackageManager.PERMISSION_GRANTED
- 申请权限
// (API) void requestPermissions (Activity activity, String[] permissions, int requestCode)
requestPermissions(arrayOf(Manifest.permission.CALL_PHONE), REQUEST_CODE_CALL_PHONE)
- 请求结果回调
// (API) void onRequestPermissionsResult (int requestCode, String[] permissions, int[] grantResults)
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
}
- 是否需要向用户解释请求权限的目的
// (API) boolean shouldShowRequestPermissionRationale (Activity activity, String permission)
ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)
情况 | 返回值 |
---|---|
第一次打开App时 | false |
上次弹出权限点击了禁止(但没有勾选“下次不在询问”) | true |
上次选择禁止并勾选“下次不在询问 ” | false |
注:如果用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了 Don’t ask again 选项,此方法将返回 false。如果设备规范禁止应用具有该权限,此方法也会返回 false。
单一权限申请交互流程
我们做移动端需要直接与用户交互,需要多考虑如何根用户交互才能达到最好的体验。下面我结合google samples中动态申请权限示例android-RuntimePermissions
https://github.com/googlesamples/android-RuntimePermissions/#readme
以及动态申请权限框架easypermissions
https://github.com/googlesamples/easypermissions
来对交互上做一个总结。
首先说明,Android不建议App直接进行拨打电话这种敏感操作,建议跳转至拨号界面,并将电话号码传入拨号界面中,这里仅作参考案例,下面每中情况都是用户从用户第一次申请权限开始(权限询问状态)
-
直接允许权限。
-
拒绝之后再次申请允许
-
不再提醒之后引导至设置界面面
话不多说,上代码。
/**
* 创建伴生对象,提供静态变量
*/
companion object {
const val TAG = "MainActivity"
const val REQUEST_CODE_CALL_PHONE = 1
}
...
// 这里进行调用requestPermmission()进行拨号前的权限请求
...
private fun callPhone() {
val intent = Intent(Intent.ACTION_CALL)
val data = Uri.parse("tel:9898123456789")
intent.data = data
startActivity(intent)
}
/**
* 提示用户申请权限说明
*/
@TargetApi(Build.VERSION_CODES.M)
fun showPermissionRationale(rationale: String) {
Snackbar.make(view, rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction("确定") {
requestPermissions(arrayOf(Manifest.permission.CALL_PHONE), REQUEST_PERMISSION_CODE_CALL_PHONE)
}.setDuration(3000)
.show()
}
/**
* 用户点击拨打电话按钮,先进行申请权限
*/
private fun requestPermmission(context: Context) {
// 判断是否需要运行时申请权限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && ContextCompat.checkSelfPermission(context, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
// 判断是否需要对用户进行提醒,用户点击过拒绝&&没有勾选不再提醒时进行提示
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) {
// 给用于予以权限解释, 对于已经拒绝过的情况,先提示申请理由,再进行申请
showPermissionRationale("需要打开电话权限直接进行拨打电话,方便您的操作")
} else {
// 无需说明理由的情况下,直接进行申请。如第一次使用该功能(第一次申请权限),用户拒绝权限并勾选了不再提醒
// 将引导跳转设置操作放在请求结果回调中处理
requestPermissions(arrayOf(Manifest.permission.CALL_PHONE), REQUEST_PERMISSION_CODE_CALL_PHONE)
}
} else {
// 拥有权限直接进行功能调用
callPhone()
}
}
/**
* 权限申请回调
*/
@TargetApi(Build.VERSION_CODES.M)
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
// 根据requestCode判断是那个权限请求的回调
if (requestCode == REQUEST_PERMISSION_CODE_CALL_PHONE) {
// 判断用户是否同意了请求
if (grantResults.size == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
callPhone(