这是 Android 官方唯一推荐的页面跳转 / 权限 / 拍照选择框架,彻底替代 startActivityForResult、onActivityResult、requestPermissions 那套老写法。
一、它是干嘛的?
替代:
- startActivityForResult()
- onActivityResult()
- requestPermissions()
优点:
- 生命周期安全(不会内存泄漏)
- 解耦(不用在 onActivityResult 里写一堆 if case)
- 链式调用、代码干净
- 官方永久推荐(老方法已废弃)
二、核心 3 个组件
- ActivityResultContract协议:你要传什么、返回什么(系统已提供很多默认实现,不用自己写)
- ActivityResultCallback回调:返回结果在这里接收
- ActivityResultLauncher启动器:真正发起跳转的对象
三、最常用的 2 个系统 Contract(90% 场景都用这俩)
1. StartActivityForResult()
跳转到另一个 Activity,接收返回结果
2. RequestPermissions()
请求权限
四、标准使用步骤(固定 3 步)
步骤 1:注册(onCreate 之前,直接定义全局变量)
kotlin
// 跳转到 SecondActivity,并接收返回结果
private val startForResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == RESULT_OK) {
val data = result.data
val name = data?.getStringExtra("name")
// 处理返回值
}
}
步骤 2:点击跳转
kotlin
btnJump.setOnClickListener {
val intent = Intent(this, SecondActivity::class.java)
startForResult.launch(intent) // 启动
}
步骤 3:第二个页面返回数据
kotlin
setResult(RESULT_OK, Intent().apply {
putExtra("name", "小明")
})
finish()
五、请求权限(超级简单)
kotlin
// 注册
private val requestPermission = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
if (isGranted) {
// 允许了
} else {
// 拒绝了
}
}
// 使用
requestPermission.launch(Manifest.permission.CAMERA)
兼容性结论(不需要再引入三方库)
✅ 完全兼容
- Android 4.1(API 16)+ 全部支持
- 无论目标版本是 Android 13 / 14 / 15 都没问题
- 无论你是 Activity / Fragment 都能用
- 无论屏幕旋转、系统重启,都能自动恢复回调,不会崩
✅ 为什么能全版本兼容?
因为:Activity Result API 是 AndroidX 库提供的兼容库不是系统原生 API,它内部自己做了全版本适配,你完全不用管版本差异。
六、请求多个权限
kotlin
private val requestMultiPermission = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { result ->
val cameraGranted = result[Manifest.permission.CAMERA] ?: false
val locationGranted = result[Manifest.permission.ACCESS_FINE_LOCATION] ?: false
}
// 调用
requestMultiPermission.launch(
arrayOf(Manifest.permission.CAMERA, Manifest.permission.ACCESS_FINE_LOCATION)
)
七、最关键知识点(必须懂)
1. registerForActivityResult 必须在 onCreate 之前注册
不能在点击事件里写!不能在延迟里写!必须作为成员变量直接初始化!
原因:系统需要在重建时恢复状态,必须提前注册。
2. 一个 launcher 对应一个功能
不要一个 launcher 乱用,会乱。规范:
- 跳转 A → 一个 launcher
- 跳转 B → 一个 launcher
- 申请权限 → 一个 launcher
3. 替代所有旧方案
- startActivityForResult → 废弃
- onActivityResult → 废弃
- requestPermissions → 废弃
全部用 Activity Result API 替代
八、最常用 Contract 大全
plaintext
StartActivityForResult() → 跳转Activity
RequestPermission() → 申请单个权限
RequestMultiplePermissions() → 申请多个权限
TakePicturePreview() → 拍照(返回缩略图)
TakePicture() → 拍照(保存到Uri)
PickContact() → 选择联系人
GetContent() → 选择文件
CreateDocument() → 创建文件
OpenDocument() → 打开文件
九、为什么 Google 要推出它?(面试必问)
- 旧版 onActivityResult 代码耦合严重
- 多个请求时需要写 requestCode 判断,容易乱
- 重建时(旋转屏幕)容易丢失回调
- 不支持 DSL、不支持解耦
- Activity Result API 是生命周期安全的,提前注册,自动恢复
十一、startForResult可以多个Activity跳转共用
一个 startForResult 可以跳转 N 个 Activity,完全没问题!
区分来源只需要:目标页面返回时带一个标记,回调里用 when 判断。
1、先明确核心结论回顾
- 一个
startForResult可以跳转任意多个 Activity - 不用每个跳转都单独定义
registerForActivityResult - 统一回调、统一通过 requestCode 请求码 区分来源页面
- 封装成工具类 + 基类,Activity 直接一行代码跳转,极度简洁
2、完整封装(基类 BaseActivity 版,项目最标准架构,优选这个)
所有 Activity 继承这个基类,全局只注册一次 Result 回调,所有页面跳转都复用这一个。
❶. 基类 BaseActivity
kotlin
import android.content.Intent
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
open class BaseActivity : AppCompatActivity() {
// ===================== 全局唯一!所有跳转全部共用这一个 =====================
private lateinit var activityResultLauncher: ActivityResultLauncher<Intent>
// 回调接口,由子类Activity实现处理返回结果
private var onActivityResult: ((resultCode: Int, data: Intent?) -> Unit)? = null
override fun onCreate(savedInstanceState: android.os.Bundle?) {
super.onCreate(savedInstanceState)
// 只在基类注册**一次**Result回调
activityResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result: ActivityResult ->
// 统一所有跳转的返回结果
onActivityResult?.invoke(result.resultCode, result.data)
// 回调用完置空,防止内存残留
onActivityResult = null
}
}
/**
* 统一跳转页面,一行代码
* @param intent 跳转意图
* @param callback 页面返回结果回调
*/
fun startForResult(intent: Intent, callback: (resultCode: Int, data: Intent?) -> Unit) {
// 赋值回调,跳转
onActivityResult = callback
activityResultLauncher.launch(intent)
}
}
❷. 所有业务 Activity 继承 BaseActivity
kotlin
class MainActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 跳 SecondActivity
btnSecond.setOnClickListener {
val intent = Intent(this, SecondActivity::class.java)
intent.putExtra("name", "测试数据")
// 一行跳转 + 直接写返回回调
startForResult(intent) { resultCode, data ->
if (resultCode == RESULT_OK) {
val name = data?.getStringExtra("name")
// 处理Second返回数据
}
}
}
// 跳 ThirdActivity,继续复用同一个!完全不用新注册
btnThird.setOnClickListener {
val intent = Intent(this, ThirdActivity::class.java)
intent.putExtra("age", 25)
startForResult(intent) { resultCode, data ->
if (resultCode == RESULT_OK) {
val age = data?.getIntExtra("age", 0)
// 处理Third返回数据
}
}
}
}
}
❸. 目标页面(Second/Third)正常返回即可,无需改动
kotlin
class SecondActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
btnBack.setOnClickListener {
val intent = Intent()
intent.putExtra("name", "我是Second返回的值")
setResult(RESULT_OK, intent)
finish()
}
}
}
3、优点(完美解决你的需求)
- 全局仅注册 1 次
registerForActivityResult不再每个跳转、每个页面都重复写一大段代码 - 任意多个 Activity 跳转全部共用Second、Third、Fourth... 多少个页面都随便跳
- Lambda 回调写法,代码清爽,不用 requestCode、不用额外标记
- 完全兼容系统原生
Intent、putExtra、getExtra - 没有内存泄漏,回调用完自动置空
- 兼容老的
RESULT_OK、RESULT_CANCELED全部逻辑
4、进阶版:带请求码区分(多数据、多页面严格区分版)
很多项目需要用 requestCode 请求码 区分不同跳转来源,我给你升级封装,支持请求码,更规范。
kotlin
import android.content.Intent
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
open class BaseActivity : AppCompatActivity() {
private lateinit var launcher: ActivityResultLauncher<Intent>
private var resultCallback: ((requestCode: Int, resultCode: Int, data: Intent?) -> Unit)? = null
private var currentRequestCode = 0
override fun onCreate(savedInstanceState: android.os.Bundle?) {
super.onCreate(savedInstanceState)
launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
resultCallback?.invoke(currentRequestCode, result.resultCode, result.data)
resultCallback = null
}
}
/**
* 带请求码跳转
*/
fun startForResult(requestCode: Int, intent: Intent, callback: (requestCode: Int, resultCode: Int, data: Intent?) -> Unit) {
currentRequestCode = requestCode
resultCallback = callback
launcher.launch(intent)
}
}
使用(定义常量请求码)
kotlin
// 请求码常量
companion object {
private const val REQUEST_SECOND = 1001
private const val REQUEST_THIRD = 1002
}
// 跳转Second
startForResult(REQUEST_SECOND, Intent(this, SecondActivity::class.java)) { reqCode, resCode, data ->
if (reqCode == REQUEST_SECOND && resCode == RESULT_OK) {
// 处理
}
}
// 跳转Third
startForResult(REQUEST_THIRD, Intent(this, ThirdActivity::class.java)) { reqCode, resCode, data ->
if (reqCode == REQUEST_THIRD && resCode == RESULT_OK) {
// 处理
}
}
5、Fragment 同样适用版本
如果你还要在 Fragment 里跳转,我也一并封装好基类 BaseFragment
kotlin
import android.content.Intent
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment
open class BaseFragment : Fragment() {
private lateinit var launcher: ActivityResultLauncher<Intent>
private var callback: ((resultCode: Int, data: Intent?) -> Unit)? = null
override fun onCreate(savedInstanceState: android.os.Bundle?) {
super.onCreate(savedInstanceState)
launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
callback?.invoke(result.resultCode, result.data)
callback = null
}
}
fun startForResult(intent: Intent, callback: (resultCode: Int, data: Intent?) -> Unit) {
callback = callback
launcher.launch(intent)
}
}
6、最终项目最佳实践总结
- 优先用基类封装版,全项目所有 Activity 共用,代码最干净
- 全程一个 ResultLauncher,不重复注册
- Lambda 回调内直接处理数据,不用额外标记、不用 requestCode
- 完全兼容 Intent 所有传参、返回、数据接收
- 完美替代废弃的
startActivityForResult()老式 API