Android Activity Result API

简介: Android Activity Result API

startActivityForResult问题

startActivityForResult(Intent(this, SecondActivity::class.java), REQ_CODE)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == REQ_CODE) {
        if (resultCode == Activity.RESULT_OK) {
            data?.let {
                val value: String? = data.getStringExtra("hello")
                log(value!!)
            }
        }
    }
}

startActivityForResult() 和 onActivityResult() 导致代码嵌套较多、耦合度高、难以维护等问题。

Google可能意识到该问题,推荐使用Activity Results API。

为什么强烈建议使用Activity Results API

官方解释

虽然所有 API 级别的 Activity 类均提供底层 startActivityForResult() 和 onActivityResult() API,但我们强烈建议您使用 AndroidX Activity和 Fragment中引入的 Activity Result API。

Activity Result API 提供了用于注册结果、启动结果以及在系统分派结果后对其进行处理的组件。

启动一个 activity(无论是本应用中的 activity 还是其他应用中的 activity)不一定是单向操作,也可以启动另一个 activity 并接收返回的结果。

在启动 activity 以获取结果时,可能会出现您的进程和 activity 因内存不足而被销毁的情况;如果是使用相机等内存密集型操作,几乎可以确定会出现这种情况。

因此,Activity Result API 会将结果回调从您之前启动另一个 activity 的代码位置分离开来。由于在重新创建进程和 activity 时需要使用结果回调,因此每次创建 activity 时都必须无条件注册回调,即使启动另一个 activity 的逻辑仅基于用户输入内容或其他业务逻辑也是如此。

通俗解释

常见的场景是调用系统相机、调用相册获取照片、调用通讯录、获取部分特殊权限等,传统方式通常是通过 Intent 携带数据,然后使用 startActivityForResult 方法来启动下一个 Activity,然后通过 onActivityResult 来接收返回的数据。

传统方式的问题在于:

1、在启动 activity 以获取结果时,可能会出现进程和 activity 因内存不足而被销毁的情况。

2、onActivityResult 回调方法嵌套耦合严重,逻辑混乱导致难以维护。

Activity Result API使用

主要有两种使用方式:

1、调用系统内置ActivityResultContract

2、调用自定义ActivityResultContract

方式一,系统内置ActivityResultContract使用

Android系统内置了常用Contract,部分列举如下

StartActivityForResult():通用Contract

RequestMultiplePermissions():申请一组权限

RequestPermission():申请单个权限

TakePicturePreview():拍照,返回Bitmap

TakePicture():拍照,保存指定Uri地址,返回true表示保存成功

TakeVideo():拍视频,保存指定Uri地址,返回一张缩略图

PickContact():从通讯录获取联系人

CreateDocument():选择一个文档,返回Uri

OpenDocumentTree():选择一个目录,返回Uri

OpenMultipleDocuments(),选择多个文档,返回多个Uri

GetContent():选择一条内容,返回Uri

StartActivityFoResult

启动一个Activity并返回数据。

注册协议

private val activityLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
    if (it.resultCode == Activity.RESULT_OK) {
        val data = it.data
        data?.apply {
            val name = getStringExtra("name")
            val age = getIntExtra("age", 0)
            val address = getStringExtra("address")
            textView.text = "name:$name age:$age address:$address"
        }
    }
}

启动

activityLauncher.launch(
    Intent(this, ThirdActivity::class.java).apply {
        putExtra("name", "小花")
        putExtra("age", 38)
        putExtra("address", "guangzhou")
    }
)

RequestPermission

申请单个权限。

注册协议

private val permissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
    if (it) {
        Toast.makeText(mContext, "权限申请-成功", Toast.LENGTH_SHORT).show()
    } else {
        Toast.makeText(mContext, "权限申请-失败", Toast.LENGTH_SHORT).show()
    }
}

启动

permissionLauncher.launch(Manifest.permission.CAMERA)

RequestMultiplePermissions

申请多个权限。

注册协议

private val multiPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { 
    it ->
    it.forEach {
        if (it.value) {
            Toast.makeText(mContext, "${it.key} 申请权限-成功", Toast.LENGTH_SHORT).show()
        } else {
            Toast.makeText(mContext, "${it.key} 申请权限-失败", Toast.LENGTH_SHORT).show()
        }
    }
}

启动

multiPermissionLauncher.launch(
    arrayOf<String>(
        Manifest.permission.CAMERA,
        Manifest.permission.CALL_PHONE,
        Manifest.permission.ACCESS_FINE_LOCATION
    )
)

TakePicturePreview

拍照。

注册协议

private val takePicturePreviewLauncher =
registerForActivityResult(ActivityResultContracts.TakePicturePreview()) {
    Toast.makeText(mContext, "Bitmap大小:${it.byteCount}", Toast.LENGTH_SHORT).show()
}

启动

takePicturePreviewLauncher.launch(null)

方式二,自定义ActivityResultContract使用

Activity Results API有三个重要的类:

ActivityResultContract:协议,这是一个抽象类,定义如何传递数据和如何接收数据,

ActivityResultLauncher:启动器,相当于以前的startActivityForResult()

ActivityResultCallback:结果回调,相当于以前的onActivityResult()

官方说明:

位于 ComponentActivity 或 Fragment 中时,Activity Result API 会提供 registerForActivityResult() API,用于注册结果回调。

registerForActivityResult() 接受 ActivityResultContract 和 ActivityResultCallback 作为参数,并返回 ActivityResultLauncher,供您用来启动另一个 activity。

ActivityResultContract 定义生成结果所需的输入类型以及结果的输出类型。这些 API 可为拍照和请求权限等基本 intent 操作提供默认协定。您还可以创建自己的自定义协定。

ActivityResultCallback 是单一方法接口,带有 onActivityResult() 方法,可接受 ActivityResultContract 中定义的输出类型的对象。

创建待启动Activity

class SecondActivity : AppCompatActivity() {
    private lateinit var textView: TextView
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
        textView = findViewById(R.id.textView)
        val name = intent.getStringExtra("name")
        textView.text = name
    }
    fun finishClick(view: View) {
        setResult(Activity.RESULT_OK, Intent().apply {
            putExtra("result", "hello ActivityResult")
        })
        finish()
    }
}

定义协议

class MyContract : ActivityResultContract<String, String>() {
    override fun createIntent(context: Context, input: String?): Intent {
        return Intent(context, SecondActivity::class.java).apply {
            putExtra("name", input)
        }
    }
    override fun parseResult(resultCode: Int, intent: Intent?): String? {
        val result = intent?.getStringExtra("result")
        return if (resultCode == Activity.RESULT_OK) {
            result
        } else {
            null
        }
    }
}

注册协议

private val launcher = registerForActivityResult(MyContract()) {
    textView.text = it
}

跳转

launcher.launch("hello world")

结果

textView.text被设置成"hello ActivityResult"

相关文章
|
存储 机器学习/深度学习 API
Android API Level 到底是什么?和安卓什么关系?应用发布如何知道自己的版本?优雅草卓伊凡
Android API Level 到底是什么?和安卓什么关系?应用发布如何知道自己的版本?优雅草卓伊凡
1995 31
Android API Level 到底是什么?和安卓什么关系?应用发布如何知道自己的版本?优雅草卓伊凡
|
编译器 API Android开发
Android经典实战之Kotlin Multiplatform 中,如何处理不同平台的 API 调用
本文介绍Kotlin Multiplatform (KMP) 中使用 `expect` 和 `actual` 关键字处理多平台API调用的方法。通过共通代码集定义预期API,各平台提供具体实现,编译器确保正确匹配,支持依赖注入、枚举类处理等,实现跨平台代码重用与原生性能。附带示例展示如何定义跨平台函数与类。
936 0
|
Android开发
Android面试题之Activity的启动模式和flag
Android Activity的四种启动模式:standard(默认,每次启动创建新实例),singleTop(栈顶复用,不走onCreate,调用onNewIntent),singleTask(栈内唯一,清除上方Activity)和singleInstance(单独栈内唯一)。启动模式在AndroidManifest.xml中配置,Intent Flags如FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_SINGLE_TOP可实现类似功能。了解这些对于处理Activity栈管理至关重要。
559 0
|
XML Android开发 数据格式
android中两个Activity同时设定了intent-filter的category为android.intent.category.LAUNCHER,会发生什么情况?
本文通过案例分析了在Android中当两个Activity都设置了`android.intent.category.LAUNCHER`类别时,会导致它们同时在应用启动器的"所有应用"页面显示为不同的启动入口。
745 2
android中两个Activity同时设定了intent-filter的category为android.intent.category.LAUNCHER,会发生什么情况?
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
471 6
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
Android面试高频知识点(4) 详解Activity的启动流程
610 3
|
缓存 前端开发 Android开发
Android实战之如何截取Activity或者Fragment的内容?
本文首发于公众号“AntDream”,介绍了如何在Android中截取Activity或Fragment的屏幕内容并保存为图片。包括截取整个Activity、特定控件或区域的方法,以及处理包含RecyclerView的复杂情况。
543 3
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
讲解Activity的启动流程了,Activity的启动流程相对复杂一下,涉及到了Activity中的生命周期方法,涉及到了Android体系的CS模式,涉及到了Android中进程通讯Binder机制等等, 首先介绍一下Activity,这里引用一下Android guide中对Activity的介绍:
607 4
|
XML Android开发 数据格式
Android 中如何设置activity的启动动画,让它像dialog一样从底部往上出来
在 Android 中实现 Activity 的对话框式过渡动画:从底部滑入与从顶部滑出。需定义两个 XML 动画文件 `activity_slide_in.xml` 和 `activity_slide_out.xml`,分别控制 Activity 的进入与退出动画。使用 `overridePendingTransition` 方法在启动 (`startActivity`) 或结束 (`finish`) Activity 时应用这些动画。为了使前 Activity 保持静止,可定义 `no_animation.xml` 并在启动新 Activity 时仅设置新 Activity 的进入动画。
1179 12
|
开发工具 Android开发
解决Manifest merger failed : android:exported needs to be explicitly specified for <activity>
解决Manifest merger failed : android:exported needs to be explicitly specified for <activity>
1134 1