权限与运行时请求
使用 ActivityResultContracts 在运行时声明并请求危险权限,显示权限说明对话框,并处理永久拒绝。
权限与运行时请求 是 CoddyKit 上的免费 Android Academy 课时。 这是第 7 节课,共 7 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Android Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Android Academy 课程共包含 7 节课。
Android 权限概述
Android 应用必须声明所需的设备功能。权限用于保护用户隐私:
- 普通权限 — 风险较低,会自动授予(INTERNET、VIBRATE)
- 危险权限 — 访问敏感数据,必须在运行时请求(CAMERA、LOCATION、READ_CONTACTS)
- 签名权限 — 仅授予使用同一证书签名的应用
在 Manifest 中声明
所有权限都必须先在 AndroidManifest.xml 中声明:
<!-- AndroidManifest.xml -->
<manifest ...>
<!-- Normal: granted automatically -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE" />
<!-- Dangerous: must request at runtime -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<application ...>
...
</application>
</manifest>检查是否已授予权限
使用危险功能前,请始终检查是否已授予相应权限:
import androidx.core.content.ContextCompat
import android.content.pm.PackageManager
fun isCameraPermissionGranted(): Boolean {
return ContextCompat.checkSelfPermission(
this, // Context
android.Manifest.permission.CAMERA
) == PackageManager.PERMISSION_GRANTED
}使用 ActivityResultLauncher 请求权限
使用 ActivityResultContracts.RequestPermission()——这是现代方式(无需使用已弃用的 onRequestPermissionsResult):
class CameraActivity : AppCompatActivity() {
private val requestCameraPermission = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
if (isGranted) {
openCamera()
} else {
showPermissionDeniedMessage()
}
}
fun onTakePhotoClick(view: View) {
if (isCameraPermissionGranted()) {
openCamera()
} else {
requestCameraPermission.launch(android.Manifest.permission.CAMERA)
}
}
}同时请求多个权限
使用 RequestMultiplePermissions 请求多个权限:
private val requestMultiple = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
val cameraGranted = permissions[android.Manifest.permission.CAMERA] ?: false
val locationGranted = permissions[android.Manifest.permission.ACCESS_FINE_LOCATION] ?: false
if (cameraGranted && locationGranted) {
startFeature()
} else {
showWhichPermissionsDenied(cameraGranted, locationGranted)
}
}
fun requestPermissions() {
requestMultiple.launch(arrayOf(
android.Manifest.permission.CAMERA,
android.Manifest.permission.ACCESS_FINE_LOCATION
))
}说明理由:何时解释原因
当用户之前拒绝过权限时,shouldShowRequestPermissionRationale() 会返回 true。再次请求前,请先显示解释:
fun requestCameraWithRationale() {
when {
isCameraPermissionGranted() -> {
openCamera()
}
shouldShowRequestPermissionRationale(android.Manifest.permission.CAMERA) -> {
// Explain why we need it, then request
AlertDialog.Builder(this)
.setTitle("Camera needed")
.setMessage("We use the camera to take profile photos.")
.setPositiveButton("OK") { _, _ ->
requestCameraPermission.launch(android.Manifest.permission.CAMERA)
}
.show()
}
else -> {
requestCameraPermission.launch(android.Manifest.permission.CAMERA)
}
}
}永久拒绝——引导至设置
如果用户点击了“不要再询问”,shouldShowRequestPermissionRationale 会返回 false,您也无法再次请求权限。请引导用户前往应用设置:
fun openAppSettings() {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.fromParts("package", packageName, null)
}
startActivity(intent)
}位置权限
位置权限分为两个级别,请根据使用场景选择正确的级别:
ACCESS_COARSE_LOCATION——城市级精度(WiFi/基站)ACCESS_FINE_LOCATION——GPS 精度ACCESS_BACKGROUND_LOCATION——在 Android 10 及更高版本中,若要在后台访问位置,则必须单独请求
相机权限完整示例
结合检查 → 说明理由 → 请求的完整流程:
class PhotoActivity : AppCompatActivity() {
private val cameraLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { granted ->
if (granted) openCamera() else showDenied()
}
private fun handleCameraClick() {
val permission = android.Manifest.permission.CAMERA
when {
ContextCompat.checkSelfPermission(this, permission) ==
PackageManager.PERMISSION_GRANTED -> openCamera()
shouldShowRequestPermissionRationale(permission) ->
showRationaleDialog()
else -> cameraLauncher.launch(permission)
}
}
private fun showDenied() {
Snackbar.make(binding.root, "Camera permission required", Snackbar.LENGTH_LONG)
.setAction("Settings") { openAppSettings() }.show()
}
}权限最佳实践
打造顺畅权限体验的关键规则:
- 仅在需要时请求权限(不要在应用启动时请求)
- 只请求所需的最低权限
- 始终妥善处理用户拒绝的情况
- 用户选择“不要再询问”后,不要反复请求
- 在系统对话框出现之前解释原因,而不是被拒绝后再解释
Android 13 及更高版本的细粒度媒体权限
从 Android 13(API 33)开始,READ_EXTERNAL_STORAGE 被拆分为更细粒度的权限:
READ_MEDIA_IMAGESREAD_MEDIA_VIDEOREAD_MEDIA_AUDIO
请始终检查 Build.VERSION.SDK_INT,并根据 OS 版本请求正确的权限。
快速检查
在请求危险权限前,何时应显示说明理由的对话框?
回顾:权限
正确处理运行时权限:
- 在
AndroidManifest.xml中声明 - 使用
ContextCompat.checkSelfPermission()检查 - 通过
registerForActivityResult(RequestPermission())请求 - 当
shouldShowRequestPermissionRationale()为 true 时显示说明理由 - 对于永久拒绝,引导用户前往 Settings
- 切勿在用户需要权限之前请求
下一步:使用 DataStore 在本地保存数据。
用 AI 导师学习 Kotlin — 免费
在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。
- 课程
- 36
- 课程
- 152
常见问题解答
「权限与运行时请求」课时是免费的吗?
是的 — 「权限与运行时请求」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Android Academy 课程的其余内容,请升级到 CoddyKit PRO。 Android Academy 课程共包含 7 节课。
「权限与运行时请求」这节课中我会学到什么?
使用 ActivityResultContracts 在运行时声明并请求危险权限,显示权限说明对话框,并处理永久拒绝。 你通过在浏览器中直接运行的动手代码来练习 Android Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Android Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Android Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 7 节课,共 7 节。
「权限与运行时请求」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Android Academy 课中编写并运行代码吗?
能。每节 Android Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。