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()
        }
    }
}
posted @ 2025-09-11 09:40  maplepie  阅读(38)  评论(0)    收藏  举报