一、核心变化(先抓主线)
旧方式(已废弃):
java
startActivityForResult(...)
onActivityResult(...)
新方式(推荐):
java
registerForActivityResult(...) → 得到 launcher
launcher.launch(...) → 发起请求
回调函数 → 收结果
👉 本质变化:
"回调注册前置 + 生命周期感知"
二、permissionLauncher 到底是什么?
你问的关键点👇
这个 Launcher 从何而来?
它其实是:
👉 一个"已经绑定好回调逻辑的请求发射器"
标准写法(单权限):
kotlin
val permissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
if (isGranted) {
// 权限允许
} else {
// 权限拒绝
}
}
拆解一下:
1️⃣ registerForActivityResult 做了什么?
干了两件事:
-
注册"请求类型"
kotlinRequestPermission() -
注册"回调函数"
kotlin{ isGranted -> ... } -
返回一个对象:
kotlinActivityResultLauncher<String>
👉 这个对象就是:
permissionLauncher
2️⃣ launcher 的本质
可以把它理解成:
launcher = 已经配置好规则的"发射器"
你之后只需要:
kotlin
permissionLauncher.launch(Manifest.permission.CALL_PHONE)
系统就会:
- 弹权限框
- 用户点"允许/拒绝"
- 自动调用你写好的回调
三、完整流程(非常重要)
把整个流程串起来👇
Step 1:先注册(必须在 onCreate 里)
kotlin
val permissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
if (isGranted) {
callPhone()
} else {
showToast("权限被拒绝")
}
}
Step 2:检查权限(和旧的一样)
kotlin
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.CALL_PHONE
) == PackageManager.PERMISSION_GRANTED
) {
callPhone()
} else {
permissionLauncher.launch(Manifest.permission.CALL_PHONE)
}
Step 3:系统处理 + 回调
用户点击:
- ✅ 允许 →
isGranted = true - ❌ 拒绝 →
isGranted = false
四、多权限版本(重点)
如果是多个权限:
kotlin
val multiLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { result: Map<String, Boolean> ->
result.forEach { (permission, granted) ->
if (granted) {
println("$permission 被允许")
} else {
println("$permission 被拒绝")
}
}
}
调用:
kotlin
multiLauncher.launch(
arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.ACCESS_FINE_LOCATION
)
)
五、为什么要换新模式?
旧方式问题:
| 问题 | 解释 |
|---|---|
| 生命周期不安全 | Activity 重建会丢回调 |
| requestCode混乱 | 手动区分 |
| 回调集中 | 一个函数处理所有逻辑 |
新方式优势:
| 优势 | 解释 |
|---|---|
| 生命周期感知 | 自动绑定 Activity |
| 类型安全 | 不用 requestCode |
| 解耦 | 每个请求独立回调 |
| 更清晰 | 逻辑分散更合理 |
六、"串起一切"是什么意思?
其实就是:
👉 按钮点击 → 检查权限 → launch → 回调 → 执行业务
比如:
kotlin
button.setOnClickListener {
if (hasPermission()) {
doSomething()
} else {
launcher.launch(permission)
}
}
👉 这就是课件说的:
"在两个按钮单击事件中串起一切"
七、权限被拒绝要做什么
课件也提到一个关键点:
👉 不只是成功逻辑,还要处理失败
比如:
kotlin
if (!isGranted) {
// 1. 禁用功能
button.isEnabled = false
// 2. 提示用户
Toast.makeText(this, "没有权限,功能不可用", Toast.LENGTH_SHORT).show()
}
八、一句话总结
👉 新权限模型本质:
先注册回调 → 再用 launcher 发请求 → 系统自动回调结果
九、代码详细分析
源码
kotlin
package com.jinxuliang.mutlipermissiomdemo
import android.Manifest
import android.content.pm.PackageManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.app.ActivityCompat
class MainActivity : AppCompatActivity() {
//用于启动一个新Activity的启动器
//泛型参数表示要向新Activity传入的数据类型
private lateinit var permissionLancher: ActivityResultLauncher<Array<String>>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val tvInfo: TextView = findViewById(R.id.tvInfo)
//注册回调函数
permissionLancher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
//处理用户权限授予结果
permissionCallback(it, tvInfo)
}
val btnShowPermissionInfo: Button = findViewById(R.id.btnShowPermissionInfo)
btnShowPermissionInfo.setOnClickListener {
//显示权限状态
showPermissionInfo(tvInfo)
}
val btnRequestPermission: Button = findViewById(R.id.btnRequestPermission)
btnRequestPermission.setOnClickListener {
//申请权限
requestPermissions()
}
}
//显示当前App的权限信息
private fun showPermissionInfo(tvInfo: TextView) {
val stringBuffer = StringBuffer()
if (hasBackgroundLocationPermission()) {
stringBuffer.append("已拥有存取后台位置信息的权限\n")
} else {
stringBuffer.append("未拥有存取后台位置信息的权限\n")
}
if (hasCameraPermission()) {
stringBuffer.append("已拥有使用摄像头的权限\n")
} else {
stringBuffer.append("未拥有使用摄像头的权限\n")
}
if (hasCallPhonePermission()) {
stringBuffer.append("已拥有打电话的权限\n")
} else {
stringBuffer.append("未拥有打电话的权限\n")
}
tvInfo.text = stringBuffer.toString()
}
//处理权限授予结果
private fun permissionCallback(
permisssions: Map<String, Boolean>,
tvInfo: TextView
) {
val stringBuffer = StringBuffer()
stringBuffer.append("权限回调函数被调用。\n")
permisssions.forEach { permission, enabled ->
stringBuffer.append("$permission 是否被授予? $enabled \n")
}
tvInfo.text = stringBuffer.toString()
}
//检测打电话的权限是否己经授予
private fun hasCallPhonePermission() =
ActivityCompat.checkSelfPermission(
this,
Manifest.permission.CALL_PHONE
) == PackageManager.PERMISSION_GRANTED
//检测使用摄像头的权限是否己经授予
private fun hasCameraPermission() =
ActivityCompat.checkSelfPermission(
this,
Manifest.permission.CAMERA
) == PackageManager.PERMISSION_GRANTED
//检测获取位置信息的权限是否己经授予
private fun hasBackgroundLocationPermission() =
ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_BACKGROUND_LOCATION
) == PackageManager.PERMISSION_GRANTED
//请求用户授予特定的权限
private fun requestPermissions() {
//获取App当前还没有权限清单
val permissionsToRequest = mutableListOf<String>()
if (!hasCallPhonePermission()) {
permissionsToRequest.add(Manifest.permission.CALL_PHONE)
}
if (!hasCameraPermission()) {
permissionsToRequest.add(Manifest.permission.CAMERA)
}
if (!hasBackgroundLocationPermission()) {
permissionsToRequest.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
}
//如果还有权限没有被授予......
if (permissionsToRequest.isNotEmpty()) {
//打开系统权限对话框,要求用户给与授权
permissionLancher.launch(permissionsToRequest.toTypedArray())
}
}
}
这段代码其实已经是 Android 新权限申请模式(Activity Result API) 的一个标准实现了
一、核心机制:permissionLauncher 是干嘛的?
kotlin
private lateinit var permissionLancher: ActivityResultLauncher<Array<String>>
👉 本质:
这是一个"异步请求权限的发射器(launcher)"
你可以理解为:
launch() 负责发起请求
回调函数 负责接收结果
二、注册回调
kotlin
permissionLancher =
registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) {
permissionCallback(it, tvInfo)
}
这里干了三件事:
1️⃣ 指定"要干什么事"
kotlin
ActivityResultContracts.RequestMultiplePermissions()
👉 表示:
我要请求 多个权限
2️⃣ 注册"结果回来后怎么办"
kotlin
{ permissionCallback(it, tvInfo) }
👉 这里的 it:
kotlin
Map<String, Boolean>
例如:
{
"android.permission.CAMERA" -> true
"android.permission.CALL_PHONE" -> false
}
3️⃣ 返回 launcher
之后你就可以用:
kotlin
permissionLancher.launch(...)
发起请求
三、真正发起权限请求
kotlin
permissionLancher.launch(permissionsToRequest.toTypedArray())
👉 等价于以前的:
java
requestPermissions(...)
但区别是:
| 旧方式 | 新方式 |
|---|---|
| requestPermissions | launch |
| onRequestPermissionsResult | 回调 lambda |
四、完整执行流程
我帮你按时间顺序梳理👇
🔹 Step 1:点击按钮
kotlin
btnRequestPermission.setOnClickListener {
requestPermissions()
}
🔹 Step 2:收集没授权的权限
kotlin
if (!hasCameraPermission()) {
permissionsToRequest.add(Manifest.permission.CAMERA)
}
👉 动态判断哪些权限还没给
🔹 Step 3:弹系统对话框
kotlin
permissionLancher.launch(...)
👉 系统弹窗:
是否允许使用摄像头?
是否允许打电话?
是否允许后台定位?
🔹 Step 4:用户操作
用户点:
- 允许 / 拒绝
🔹 Step 5:回调触发
kotlin
permissionCallback(it, tvInfo)
五、回调函数在干嘛?
kotlin
private fun permissionCallback(
permisssions: Map<String, Boolean>,
tvInfo: TextView
)
👉 参数含义:
| key | value |
|---|---|
| 权限名 | 是否授权 |
示例输出:
权限回调函数被调用。
android.permission.CAMERA 是否被授予? true
android.permission.CALL_PHONE 是否被授予? false
六、三个权限检测函数
例如:
kotlin
private fun hasCameraPermission() =
ActivityCompat.checkSelfPermission(
this,
Manifest.permission.CAMERA
) == PackageManager.PERMISSION_GRANTED
👉 本质就是:
系统有没有给你这个权限?
返回:
- true → 有权限
- false → 没权限
七、这段代码的设计规范
它体现了三个重要思想:
✅ 1. "先检查,再申请"
kotlin
if (!hasCameraPermission())
👉 避免重复弹窗
✅ 2. "按需申请"
只申请没给的权限
✅ 3. "结果集中处理"
kotlin
permissionCallback()
👉 所有权限结果统一处理
八、一个容易踩坑的点
❗ Android 10+ 限制:
kotlin
ACCESS_BACKGROUND_LOCATION
👉 不能和前台定位一起申请!
必须:
先申请前台定位
再单独申请后台定位
否则:
👉 很可能直接被系统拒绝
九、你可以这样理解整套机制
一句话总结:
register → 定义回调
launch → 发起请求
callback → 接收结果