Activity Result API 的使用:

28 阅读6分钟

这是 Android 官方唯一推荐的页面跳转 / 权限 / 拍照选择框架,彻底替代 startActivityForResult、onActivityResult、requestPermissions 那套老写法。

一、它是干嘛的?

替代:

  • startActivityForResult()
  • onActivityResult()
  • requestPermissions()

优点:

  1. 生命周期安全(不会内存泄漏)
  2. 解耦(不用在 onActivityResult 里写一堆 if case)
  3. 链式调用、代码干净
  4. 官方永久推荐(老方法已废弃)

二、核心 3 个组件

  1. ActivityResultContract协议:你要传什么、返回什么(系统已提供很多默认实现,不用自己写)
  2. ActivityResultCallback回调:返回结果在这里接收
  3. 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 要推出它?(面试必问)

  1. 旧版 onActivityResult 代码耦合严重
  2. 多个请求时需要写 requestCode 判断,容易乱
  3. 重建时(旋转屏幕)容易丢失回调
  4. 不支持 DSL、不支持解耦
  5. Activity Result API 是生命周期安全的,提前注册,自动恢复

十一、startForResult可以多个Activity跳转共用

一个 startForResult 可以跳转 N 个 Activity,完全没问题!
区分来源只需要:目标页面返回时带一个标记,回调里用 when 判断
1、先明确核心结论回顾
  1. 一个 startForResult 可以跳转任意多个 Activity
  2. 不用每个跳转都单独定义 registerForActivityResult
  3. 统一回调、统一通过 requestCode 请求码 区分来源页面
  4. 封装成工具类 + 基类,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. 全局仅注册 1 次 registerForActivityResult 不再每个跳转、每个页面都重复写一大段代码
  2. 任意多个 Activity 跳转全部共用Second、Third、Fourth... 多少个页面都随便跳
  3. Lambda 回调写法,代码清爽,不用 requestCode、不用额外标记
  4. 完全兼容系统原生 IntentputExtragetExtra
  5. 没有内存泄漏,回调用完自动置空
  6. 兼容老的 RESULT_OKRESULT_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、最终项目最佳实践总结
  1. 优先用基类封装版,全项目所有 Activity 共用,代码最干净
  2. 全程一个 ResultLauncher,不重复注册
  3. Lambda 回调内直接处理数据,不用额外标记、不用 requestCode
  4. 完全兼容 Intent 所有传参、返回、数据接收
  5. 完美替代废弃的 startActivityForResult() 老式 API