Android 权限(Permission)完整指南

发布: (2025年12月31日 GMT+8 12:22)
5 min read
原文: Dev.to

Source: Dev.to

权限类型

Protection Level

  • Normal: 安装时自动授予,无需用户批准
    例如:INTERNET, VIBRATE, ACCESS_NETWORK_STATE

  • Dangerous: 运行时需要用户批准
    例如:CAMERA, READ_CONTACTS, ACCESS_FINE_LOCATION

  • Signature: 仅限相同签名的应用使用
    例如:BIND_ACCESSIBILITY_SERVICE

运行时权限

Android 6.0 (API 23)+ 以及 targetSdkVersion 23 以上时需要。

权限检查

if (ContextCompat.checkSelfPermission(
        this,
        Manifest.permission.CAMERA
    ) == PackageManager.PERMISSION_GRANTED
) {
    // 권한 있음
    openCamera()
} else {
    // 권한 요청 필요
    requestCameraPermission()
}

权限请求

private val requestPermissionLauncher = registerForActivityResult(
    ActivityResultContracts.RequestPermission()
) { isGranted ->
    if (isGranted) {
        openCamera()
    } else {
        showPermissionDeniedMessage()
    }
}

private fun requestCameraPermission() {
    when {
        shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) -> {
            // 권한 필요 이유 설명 후 요청
            showPermissionRationale()
        }
        else -> {
            requestPermissionLauncher.launch(Manifest.permission.CAMERA)
        }
    }
}

一次请求多个权限

private val requestMultiplePermissions = registerForActivityResult(
    ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
    permissions.entries.forEach { (permission, isGranted) ->
        when {
            isGranted -> Log.d(TAG, "$permission granted")
            shouldShowRequestPermissionRationale(permission) -> {
                // 거부됨, 다시 설명 가능
            }
            else -> {
                // 영구 거부됨
            }
        }
    }
}

// 요청
requestMultiplePermissions.launch(
    arrayOf(
        Manifest.permission.CAMERA,
        Manifest.permission.RECORD_AUDIO
    )
)

权限流程

检查权限 -> 无 -> shouldShowRationale?
                      -> Yes: 显示说明 -> 请求权限
                      -> No: 请求权限
                            -> 允许: 使用功能
                            -> 拒绝:
                                -> shouldShowRationale = true: 可再次请求
                                -> shouldShowRationale = false: 永久拒绝

永久拒绝处理

private fun handlePermanentDenial() {
    AlertDialog.Builder(this)
        .setTitle("권한 필요")
        .setMessage("이 기능을 사용하려면 설정에서 권한을 허용해주세요.")
        .setPositiveButton("설정으로 이동") { _, _ ->
            openAppSettings()
        }
        .setNegativeButton("취소", null)
        .show()
}

private fun openAppSettings() {
    Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
        data = Uri.fromParts("package", packageName, null)
        startActivity(this)
    }
}

权限组

如果允许同一组中的任意一个权限,组内的其他权限也会自动被允许。
注意:权限组可能会发生变化,请单独检查每个权限。

硬件功能

if (packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
    // 카메라 기능 사용 가능
}

BroadcastReceiver 权限

仅接收具有特定权限的应用的广播:

<receiver android:name=".MyReceiver">
    <intent-filter>
        <action android:name="android.intent.action.SOME_ACTION"/>
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.ANOTHER_ACTION"/>
    </intent-filter>
    <meta-data
        android:name="android.permission.SOME_PERMISSION"
        android:value="true"/>
</receiver>

使用 ADB 测试权限

# 授予权限
adb shell pm grant com.example.app android.permission.CAMERA

# 撤销权限
adb shell pm revoke com.example.app android.permission.CAMERA

Permission Helper 类

class PermissionHelper(private val activity: ComponentActivity) {

    private var callback: ((Boolean) -> Unit)? = null

    private val launcher = activity.registerForActivityResult(
        ActivityResultContracts.RequestMultiplePermissions()
    ) { permissions ->
        val allGranted = permissions.values.all { it }
        callback?.invoke(allGranted)
    }

    fun requestPermissions(
        permissions: Array<String>,
        onResult: (Boolean) -> Unit
    ) {
        callback = onResult

        val notGranted = permissions.filter {
            ContextCompat.checkSelfPermission(activity, it) !=
                PackageManager.PERMISSION_GRANTED
        }

        if (notGranted.isEmpty()) {
            onResult(true)
            return
        }

        launcher.launch(notGranted.toTypedArray())
    }

    fun hasPermission(permission: String): Boolean {
        return ContextCompat.checkSelfPermission(activity, permission) ==
            PackageManager.PERMISSION_GRANTED
    }

    fun shouldShowRationale(permission: String): Boolean {
        return activity.shouldShowRequestPermissionRationale(permission)
    }
}

使用示例

class MainActivity : AppCompatActivity() {
    private lateinit var permissionHelper: PermissionHelper

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        permissionHelper = PermissionHelper(this)

        // 示例:请求摄像头和音频权限
        permissionHelper.requestPermissions(
            arrayOf(
                Manifest.permission.CAMERA,
                Manifest.permission.RECORD_AUDIO
            )
        ) { allGranted ->
            if (allGranted) {
                // 所有权限已授予
                startRecording()
            } else {
                // 权限被拒绝
                showPermissionDeniedMessage()
            }
        }
    }

    private fun startRecording() {
        // 录音/拍摄逻辑
    }

    private fun showPermissionDeniedMessage() {
        Toast.makeText(this, "需要权限。", Toast.LENGTH_SHORT).show()
    }
}

代码示例

missionHelper = PermissionHelper(this)
}

private fun checkCameraPermission() {
    permissionHelper.requestPermissions(
        arrayOf(Manifest.permission.CAMERA)
    ) { granted ->
        if (granted) {
            openCamera()
        } else {
            handlePermissionDenied()
        }
    }
}

在没有 SMS 权限的情况下接收 OTP – 使用 SMS Retriever API

val client = SmsRetriever.getClient(this)
client.startSmsRetriever()
    .addOnSuccessListener { /* 监听器已启动 */ }
    .addOnFailureListener { /* 失败 */ }

注意事项

  • 权限组可更改:对每个权限单独勾选
  • 可在设置中解除:即使之前已允许,也需要重新勾选
  • 拒绝权限时应用会重启onTerminate() 不会被调用
  • 授权后再拒绝:永久拒绝记录会被重置

结论

运行时权限对于保护用户隐私非常重要。请明确说明请求权限的原因,仅在必要时请求,并在被拒绝时提供替代流程。

Back to Blog

相关文章

阅读更多 »

新年快乐,社区!

引言 大家好,感谢阅读本博客,祝大家新年快乐! 项目概述 本项目的最终目标是一个纯粹的...