Android Kotlin请求权限及权限回调处理
class MainActivity : AppCompatActivity() {
// 请求权限的标识
private val REQUEST_PERMISSIONS_CODE = 100
// 需请求的权限
private val REQUIRED_PERMISSIONS = arrayOf(
Manifest.permission.INTERNET,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
// 检查权限
private fun allPermissionsGranted(): Boolean {
// 检查权限是否已授权
val internetGranted = ContextCompat.checkSelfPermission(baseContext, Manifest.permission.INTERNET) == PackageManager.PERMISSION_GRANTED
// 仅在特定版本检查授权,否则按已授权处理
val storageGranted = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
ContextCompat.checkSelfPermission(baseContext, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
} else {
true
}
return internetGranted && storageGranted
}
// 授权结果处理
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
// 仅处理有对应权限标识的逻辑
if (requestCode == REQUEST_PERMISSIONS_CODE) {
// 再次检查是否所有权限已授权
if (allPermissionsGranted()) {
doSomething()
} else {
Toast.makeText(this, "权限被拒绝,应用无法正常工作", Toast.LENGTH_LONG).show()
finish()
}
}
}
public override fun onStart() {
super.onStart()
// 检查是否所有权限已授权
if (allPermissionsGranted()) {
doSomething()
} else {
// 存在未授权的权限时请求获取权限
ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_PERMISSIONS_CODE)
}
}
}
封装权限请求
PermissionHelper.kt
package com.example.permissiondemo
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.provider.Settings
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.ActivityResultRegistry
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleOwner
/**
* 封装权限请求和处理逻辑的帮助类。
*
* @param lifecycleOwner 用于注册 ActivityResultLauncher 的生命周期所有者 (通常是 Activity 或 Fragment)。
* @param activityContext AppCompatActivity 实例,用于权限检查、显示 Toast、AlertDialog 和 shouldShowRequestPermissionRationale。
* @param registry ActivityResultRegistry 实例,用于注册 ActivityResultLauncher。
* @param requestKey 一个唯一的字符串标识符,用于注册 ActivityResultLauncher,避免多个请求混合。
* @param permissionsToRequest 需要请求的危险权限数组。
* @param callback 权限请求结果的回调接口。
*/
class PermissionHelper(
private val lifecycleOwner: LifecycleOwner,
private val activityContext: AppCompatActivity,
private val registry: ActivityResultRegistry,
private val requestKey: String, // <-- 新增:外部传入唯一的请求 key
private val permissionsToRequest: Array<String>,
private val callback: PermissionCallback
) {
// ActivityResultLauncher 用于处理多个权限请求
private val requestPermissionsLauncher: ActivityResultLauncher<Array<String>>
init {
// 在 init 块中注册 ActivityResultLauncher
requestPermissionsLauncher = registry.register(
requestKey, // <-- 使用外部传入的唯一 key
lifecycleOwner,
ActivityResultContracts.RequestMultiplePermissions()
) { permissionsMap ->
handlePermissionResult(permissionsMap)
}
}
/**
* 权限请求结果的回调接口。
*/
interface PermissionCallback {
/**
* 所有必要的危险权限都已授予。
*/
fun onAllPermissionsGranted()
/**
* 部分或所有权限被拒绝,但用户没有勾选“不再询问”。
* @param deniedPermissions 被拒绝的权限列表。
*/
fun onPermissionsDenied(deniedPermissions: List<String>)
/**
* 部分或所有权限被永久拒绝(用户勾选了“不再询问”)。
* @param deniedPermanently 被永久拒绝的权限列表。
*/
fun onPermissionsDeniedPermanently(deniedPermanently: List<String>)
}
/**
* 检查并请求权限。
* 如果所有权限都已授予,则立即回调 onAllPermissionsGranted。
* 否则,将启动权限请求流程。
*/
fun checkAndRequestPermissions() {
if (allDangerousPermissionsGranted()) {
// 所有权限已授予,直接回调
callback.onAllPermissionsGranted()
} else {
// 筛选出未授予的权限,只请求这些
val permissionsToRequestNow = permissionsToRequest.filter { permission ->
ContextCompat.checkSelfPermission(activityContext, permission) != PackageManager.PERMISSION_GRANTED
}.toTypedArray()
// 检查是否需要向用户解释权限
val shouldShowRationale = permissionsToRequestNow.any { permission ->
// 直接使用传入的 activityContext 调用 shouldShowRequestPermissionRationale
ActivityCompat.shouldShowRequestPermissionRationale(activityContext, permission)
}
if (shouldShowRationale) {
// 显示解释对话框
AlertDialog.Builder(activityContext) // 使用 activityContext
.setTitle("需要权限")
.setMessage("此应用需要必要的权限才能正常工作。请授予权限以继续。")
.setPositiveButton("好的") { _, _ ->
requestPermissionsLauncher.launch(permissionsToRequestNow)
}
.setNegativeButton("取消") { dialog, _ ->
dialog.dismiss()
Toast.makeText(activityContext, "权限被拒绝,应用无法正常工作。", Toast.LENGTH_LONG).show()
// 回调拒绝,但不标记为永久拒绝
callback.onPermissionsDenied(permissionsToRequestNow.toList())
}
.show()
} else {
// 直接请求权限
requestPermissionsLauncher.launch(permissionsToRequestNow)
}
}
}
/**
* 检查是否所有必要的危险权限已授权。
*/
private fun allDangerousPermissionsGranted(): Boolean {
return permissionsToRequest.all { permission ->
ContextCompat.checkSelfPermission(activityContext, permission) == PackageManager.PERMISSION_GRANTED
}
}
/**
* 处理权限请求结果。
*/
private fun handlePermissionResult(permissionsMap: Map<String, Boolean>) {
// 检查所有在初始 permissionsToRequest 数组中的权限是否都已授予
val allGranted = permissionsToRequest.all { permission ->
permissionsMap[permission] == true // 检查权限是否在map中且值为true
}
if (allGranted) {
callback.onAllPermissionsGranted()
} else {
val deniedPermissions = permissionsToRequest.filter { permissionsMap[it] == false }
val deniedPermanently = deniedPermissions.filter { permission ->
// 权限被拒绝 且 不能再显示权限解释(即用户勾选了不再询问或系统默认)
ActivityCompat.shouldShowRequestPermissionRationale(activityContext, permission) == false
}
if (deniedPermanently.isNotEmpty()) {
// 权限被永久拒绝,引导用户去设置中手动开启
showPermissionDeniedPermanentlyDialog(deniedPermanently)
callback.onPermissionsDeniedPermanently(deniedPermanently)
} else {
// 权限被拒绝,但用户可能下次还会授予 (没有勾选“不再询问”)
Toast.makeText(activityContext, "部分权限被拒绝,某些功能可能无法使用。", Toast.LENGTH_LONG).show()
callback.onPermissionsDenied(deniedPermissions)
}
}
}
/**
* 显示权限被永久拒绝的对话框,引导用户去设置中开启。
*/
private fun showPermissionDeniedPermanentlyDialog(deniedPermissions: List<String>) {
AlertDialog.Builder(activityContext) // 使用 activityContext
.setTitle("权限被拒绝")
.setMessage("您已永久拒绝了重要权限 (${deniedPermissions.joinToString()})。请到“设置 -> 应用 -> [您的应用] -> 权限”中手动开启。")
.setPositiveButton("去设置") { _, _ ->
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", activityContext.packageName, null)
intent.data = uri
activityContext.startActivity(intent)
}
.setNegativeButton("取消") { dialog, _ ->
dialog.dismiss()
// 不执行 finish(),让调用方决定如何处理
}
.show()
}
}
MainActivity.kt (示例 Activity)
package com.example.permissiondemo
import android.Manifest
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
class MainActivity : AppCompatActivity() {
// 为不同的权限组定义唯一的请求 key
private val STORAGE_PERMISSION_KEY = "StoragePermissionRequest"
private val CAMERA_PERMISSION_KEY = "CameraPermissionRequest"
private lateinit var storagePermissionHelper: PermissionHelper
private lateinit var cameraPermissionHelper: PermissionHelper
private val STORAGE_PERMISSIONS = arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)
private val CAMERA_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 初始化存储权限的 PermissionHelper
storagePermissionHelper = PermissionHelper(
this,
this,
activityResultRegistry,
STORAGE_PERMISSION_KEY, // 传入唯一的 key
STORAGE_PERMISSIONS,
object : PermissionHelper.PermissionCallback { // 使用匿名内部类实现回调
override fun onAllPermissionsGranted() {
Toast.makeText(this@MainActivity, "MainActivity: 存储权限已授予!", Toast.LENGTH_SHORT).show()
doSomethingWithStorage()
}
override fun onPermissionsDenied(deniedPermissions: List<String>) {
Toast.makeText(this@MainActivity, "MainActivity: 存储权限 (${deniedPermissions.joinToString()}) 被拒绝。", Toast.LENGTH_LONG).show()
// 处理存储权限被拒绝的情况
}
override fun onPermissionsDeniedPermanently(deniedPermanently: List<String>) {
Toast.makeText(this@MainActivity, "MainActivity: 存储权限 (${deniedPermanently.joinToString()}) 被永久拒绝。", Toast.LENGTH_LONG).show()
// 处理存储权限被永久拒绝的情况
}
}
)
// 初始化相机权限的 PermissionHelper
cameraPermissionHelper = PermissionHelper(
this,
this,
activityResultRegistry,
CAMERA_PERMISSION_KEY, // 传入另一个唯一的 key
CAMERA_PERMISSIONS,
object : PermissionHelper.PermissionCallback { // 使用匿名内部类实现回调
override fun onAllPermissionsGranted() {
Toast.makeText(this@MainActivity, "MainActivity: 相机权限已授予!", Toast.LENGTH_SHORT).show()
doSomethingWithCamera()
}
override fun onPermissionsDenied(deniedPermissions: List<String>) {
Toast.makeText(this@MainActivity, "MainActivity: 相机权限 (${deniedPermissions.joinToString()}) 被拒绝。", Toast.LENGTH_LONG).show()
// 处理相机权限被拒绝的情况
}
override fun onPermissionsDeniedPermanently(deniedPermanently: List<String>) {
Toast.makeText(this@MainActivity, "MainActivity: 相机权限 (${deniedPermanently.joinToString()}) 被永久拒绝。", Toast.LENGTH_LONG).show()
// 处理相机权限被永久拒绝的情况
}
}
)
val requestStoragePermissionButton: Button = findViewById(R.id.requestStoragePermissionButton)
requestStoragePermissionButton.setOnClickListener {
storagePermissionHelper.checkAndRequestPermissions()
}
val requestCameraPermissionButton: Button = findViewById(R.id.requestCameraPermissionButton)
requestCameraPermissionButton.setOnClickListener {
cameraPermissionHelper.checkAndRequestPermissions()
}
}
// --- 各自权限授予后执行的操作 ---
private fun doSomethingWithStorage() {
Toast.makeText(this, "MainActivity: 正在执行需要存储权限的操作...", Toast.LENGTH_SHORT).show()
// 实际的存储操作
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
// 示例:可以尝试写入一个文件等
Toast.makeText(this, "MainActivity: 可以执行存储写入操作了。", Toast.LENGTH_SHORT).show()
}
}
private fun doSomethingWithCamera() {
Toast.makeText(this, "MainActivity: 正在执行需要相机权限的操作...", Toast.LENGTH_SHORT).show()
// 实际的相机操作
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
// 示例:可以启动相机预览等
Toast.makeText(this, "MainActivity: 可以使用相机了。", Toast.LENGTH_SHORT).show()
}
}
}
MyFragment.kt (示例 Fragment)
package com.example.permissiondemo
import android.Manifest
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
class MyFragment : Fragment() {
// 为不同的权限组定义唯一的请求 key
private val LOCATION_PERMISSION_KEY = "LocationPermissionRequest"
private val CONTACTS_PERMISSION_KEY = "ContactsPermissionRequest"
private lateinit var locationPermissionHelper: PermissionHelper
private lateinit var contactsPermissionHelper: PermissionHelper
private val LOCATION_PERMISSIONS = arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
private val CONTACTS_PERMISSIONS = arrayOf(Manifest.permission.READ_CONTACTS)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_my, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 确保 Fragment 依附的 Activity 是 AppCompatActivity
val parentActivity = requireActivity() as AppCompatActivity
// 初始化位置权限的 PermissionHelper
locationPermissionHelper = PermissionHelper(
this, // Fragment 作为 LifecycleOwner
parentActivity, // 依附的 AppCompatActivity 实例
activityResultRegistry, // Fragment 的 ActivityResultRegistry
LOCATION_PERMISSION_KEY,
LOCATION_PERMISSIONS,
object : PermissionHelper.PermissionCallback {
override fun onAllPermissionsGranted() {
Toast.makeText(requireContext(), "MyFragment: 位置权限已授予!", Toast.LENGTH_SHORT).show()
doSomethingWithLocation()
}
override fun onPermissionsDenied(deniedPermissions: List<String>) {
Toast.makeText(requireContext(), "MyFragment: 位置权限 (${deniedPermissions.joinToString()}) 被拒绝。", Toast.LENGTH_LONG).show()
}
override fun onPermissionsDeniedPermanently(deniedPermanently: List<String>) {
Toast.makeText(requireContext(), "MyFragment: 位置权限 (${deniedPermanently.joinToString()}) 被永久拒绝。", Toast.LENGTH_LONG).show()
}
}
)
// 初始化联系人权限的 PermissionHelper
contactsPermissionHelper = PermissionHelper(
this, // Fragment 作为 LifecycleOwner
parentActivity, // 依附的 AppCompatActivity 实例
activityResultRegistry, // Fragment 的 ActivityResultRegistry
CONTACTS_PERMISSION_KEY,
CONTACTS_PERMISSIONS,
object : PermissionHelper.PermissionCallback {
override fun onAllPermissionsGranted() {
Toast.makeText(requireContext(), "MyFragment: 联系人权限已授予!", Toast.LENGTH_SHORT).show()
doSomethingWithContacts()
}
override fun onPermissionsDenied(deniedPermissions: List<String>) {
Toast.makeText(requireContext(), "MyFragment: 联系人权限 (${deniedPermissions.joinToString()}) 被拒绝。", Toast.LENGTH_LONG).show()
}
override fun onPermissionsDeniedPermanently(deniedPermanently: List<String>) {
Toast.makeText(requireContext(), "MyFragment: 联系人权限 (${deniedPermanently.joinToString()}) 被永久拒绝。", Toast.LENGTH_LONG).show()
}
}
)
val requestLocationButton: Button = view.findViewById(R.id.requestLocationButton)
requestLocationButton.setOnClickListener {
locationPermissionHelper.checkAndRequestPermissions()
}
val requestContactsButton: Button = view.findViewById(R.id.requestContactsButton)
requestContactsButton.setOnClickListener {
contactsPermissionHelper.checkAndRequestPermissions()
}
}
// --- 各自权限授予后执行的操作 ---
private fun doSomethingWithLocation() {
Toast.makeText(requireContext(), "MyFragment: 正在执行需要位置权限的操作...", Toast.LENGTH_SHORT).show()
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
// 示例:获取当前位置
Toast.makeText(requireContext(), "MyFragment: 可以获取精确位置了。", Toast.LENGTH_SHORT).show()
}
}
private fun doSomethingWithContacts() {
Toast.makeText(requireContext(), "MyFragment: 正在执行需要联系人权限的操作...", Toast.LENGTH_SHORT).show()
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
// 示例:读取联系人
Toast.makeText(requireContext(), "MyFragment: 可以读取联系人了。", Toast.LENGTH_SHORT).show()
}
}
}
本文来自博客园,作者:maplepie,转载请注明原文链接:https://www.cnblogs.com/maplepie/p/19085131

浙公网安备 33010602011771号