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()

官方说明:

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

registerForActivityResult() 接受 ActivityResultContractActivityResultCallback 作为参数,并返回 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"

相关文章
|
6月前
|
编译器 API Android开发
Android经典实战之Kotlin Multiplatform 中,如何处理不同平台的 API 调用
本文介绍Kotlin Multiplatform (KMP) 中使用 `expect` 和 `actual` 关键字处理多平台API调用的方法。通过共通代码集定义预期API,各平台提供具体实现,编译器确保正确匹配,支持依赖注入、枚举类处理等,实现跨平台代码重用与原生性能。附带示例展示如何定义跨平台函数与类。
176 0
|
7月前
|
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栈管理至关重要。
65 0
|
4月前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
108 6
|
4月前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
Android面试高频知识点(4) 详解Activity的启动流程
39 3
|
4月前
|
缓存 前端开发 Android开发
Android实战之如何截取Activity或者Fragment的内容?
本文首发于公众号“AntDream”,介绍了如何在Android中截取Activity或Fragment的屏幕内容并保存为图片。包括截取整个Activity、特定控件或区域的方法,以及处理包含RecyclerView的复杂情况。
37 3
|
4月前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
28 0
|
5月前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
讲解Activity的启动流程了,Activity的启动流程相对复杂一下,涉及到了Activity中的生命周期方法,涉及到了Android体系的CS模式,涉及到了Android中进程通讯Binder机制等等, 首先介绍一下Activity,这里引用一下Android guide中对Activity的介绍:
77 4
|
6月前
|
XML Android开发 数据格式
android中两个Activity同时设定了intent-filter的category为android.intent.category.LAUNCHER,会发生什么情况?
本文通过案例分析了在Android中当两个Activity都设置了`android.intent.category.LAUNCHER`类别时,会导致它们同时在应用启动器的"所有应用"页面显示为不同的启动入口。
169 2
android中两个Activity同时设定了intent-filter的category为android.intent.category.LAUNCHER,会发生什么情况?
|
5月前
|
Android开发 开发者
Android面试之Activity启动流程简述
每个Android开发者都熟悉的Activity,但你是否了解它的启动流程呢?本文将带你深入了解。启动流程涉及四个关键角色:Launcher进程、SystemServer的AMS、应用程序的ActivityThread及Zygote进程。核心在于AMS与ActivityThread间的通信。文章详细解析了从Launcher启动Activity的过程,包括通过AIDL获取AMS、Zygote进程启动以及ActivityThread与AMS的通信机制。接着介绍了如何创建Application及Activity的具体步骤。整体流程清晰明了,帮助你更深入理解Activity的工作原理。
81 0
|
6月前
|
开发工具 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>
142 1

热门文章

最新文章