当前位置: 首页 > news >正文

registerForActivityResult使用

目录

针对 activity 结果注册回调

启动 activity 以获取其结果

在单独的类中接收 activity 结果

测试

创建自定义协定


registerForActivityResult()是startActivityForResult()的替代,简化了数据回调的写法

启动另一个 activity(无论是您应用中的 activity 还是其他应用中的 activity)不一定是单向操作。您也可以启动另一个 activity 并接收返回的结果。例如,您的应用可启动相机应用并接收拍摄的照片作为结果。或者,您可以启动“通讯录”应用以便用户选择联系人,并且您将接收联系人详细信息作为结果。

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

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

针对 activity 结果注册回调

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

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

位于 ComponentActivity 或 Fragment 中时,Activity Result API 会提供 registerForActivityResult() API,用于注册结果回调。registerForActivityResult() 接受 ActivityResultContract 和 ActivityResultCallback 作为参数,并返回 ActivityResultLauncher,供您用来启动另一个 activity。

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

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

val getContent = registerForActivityResult(GetContent()) { uri: Uri? ->     // Handle the returned Uri }

如果您有多个使用不同协定或需要单独回调的 activity 结果调用,则可以多次调用 registerForActivityResult(),以注册多个 ActivityResultLauncher 实例。每次创建 fragment 或 activity 时,都必须按照相同的顺序调用 registerForActivityResult(),才能确保将生成的结果传递给正确的回调。

在 fragment 或 activity 创建完毕之前可安全地调用 registerForActivityResult(),因此,在为返回的 ActivityResultLauncher 实例声明成员变量时可以直接使用它。

注意:您必须在创建 fragment 或 activity 之前调用 registerForActivityResult();在 fragment 或 activity 的 Lifecycle 达到 CREATED 之前,您将无法启动 ActivityResultLauncher

启动 activity 以获取其结果

虽然 registerForActivityResult() 会注册您的回调,但它不会启动另一个 activity 并发出结果请求。这些操作由返回的 ActivityResultLauncher 实例负责。

如果存在输入内容,启动器会接受与 ActivityResultContract 的类型匹配的输入内容。调用 launch() 会启动生成结果的过程。当用户完成后续 activity 并返回时,系统将执行 ActivityResultCallback 中的 onActivityResult(),如以下示例所示:

val getContent = registerForActivityResult(GetContent()) { uri: Uri? ->     // Handle the returned Uri } override fun onCreate(savedInstanceState: Bundle?) {     // ...     val selectButton = findViewById<Button>(R.id.select_button)     selectButton.setOnClickListener {         // Pass in the mime type you'd like to allow the user to select         // as the input         getContent.launch("image/*")     } }

除了传递输入内容之外,launch() 的重载版本还允许您传递 ActivityOptionsCompat。

注意:由于在调用 launch() 与触发 onActivityResult() 回调的两个时间点之间,您的进程和 activity 可能会被销毁,因此,处理结果所需的任何其他状态都必须与这些 API 分开保存和恢复。

在单独的类中接收 activity 结果

虽然 ComponentActivity 和 Fragment 类通过实现 ActivityResultCaller 接口来允许您使用 registerForActivityResult() API,但您也可以直接使用 ActivityResultRegistry 在未实现 ActivityResultCaller 的单独类中接收结果。

例如,您可能需要实现一个 LifecycleObserver,用于处理协定的注册和启动器的启动:

class MyLifecycleObserver(private val registry : ActivityResultRegistry)         : DefaultLifecycleObserver {     lateinit var getContent : ActivityResultLauncher<String>     override fun onCreate(owner: LifecycleOwner) {         getContent = registry.register("key", owner, GetContent()) { uri ->             // Handle the returned Uri         }     }     fun selectImage() {         getContent.launch("image/*")     } } class MyFragment : Fragment() {     lateinit var observer : MyLifecycleObserver     override fun onCreate(savedInstanceState: Bundle?) {         // ...         observer = MyLifecycleObserver(requireActivity().activityResultRegistry)         lifecycle.addObserver(observer)     }     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {         val selectButton = view.findViewById<Button>(R.id.select_button)         selectButton.setOnClickListener {             // Open the activity to select an image             observer.selectImage()         }     } }

使用 ActivityResultRegistry API 时,强烈建议您使用可接受 LifecycleOwner 作为参数的 API,因为 LifecycleOwner 会在 Lifecycle 被销毁时自动移除已注册的启动器。不过,如果 LifecycleOwner 不存在,每个 ActivityResultLauncher 类都允许您手动调用 unregister() 作为替代。

测试

默认情况下,registerForActivityResult() 会自动使用 activity 提供的 ActivityResultRegistry。此外,它还提供了一个重载,让您可以传入自己的 ActivityResultRegistry 实例,该实例可用于测试您的 activity 结果调用,无需实际启动另一个 activity。

测试应用的 fragment 时,使用 FragmentFactory 将 ActivityResultRegistry 传入该 fragment 的构造函数即可提供测试 ActivityResultRegistry

注意:任何允许您在测试中注入单独 ActivityResultRegistry 的机制都足以支持您测试 activity 结果调用。

例如,使用 TakePicturePreview 协定获取图片缩略图的 fragment 可能按类似如下所示的方式编写:

class MyFragment(     private val registry: ActivityResultRegistry ) : Fragment() {     val thumbnailLiveData = MutableLiveData<Bitmap?>     val takePicture = registerForActivityResult(TakePicturePreview(), registry) {         bitmap: Bitmap? -> thumbnailLiveData.setValue(bitmap)     }     // ... }

创建专用于测试的 ActivityResultRegistry 时,必须实现 onLaunch() 方法。您的测试实现可以直接调用 dispatchResult(),而不是调用 startActivityForResult(),从而提供要在测试中使用的确切结果:

val testRegistry = object : ActivityResultRegistry() {override fun <I, O> onLaunch(requestCode: Int,contract: ActivityResultContract<I, O>,input: I,options: ActivityOptionsCompat?) {dispatchResult(requestCode, expectedResult)}
}

完整的测试会产生预期的结果,构造一个测试 ActivityResultRegistry,将其传递给 fragment,直接触发启动器或通过 Espresso 等其他测试 API 触发启动器,然后验证结果:

@Test
fun activityResultTest {// Create an expected result Bitmapval expectedResult = Bitmap.createBitmap(1, 1, Bitmap.Config.RGBA_F16)// Create the test ActivityResultRegistryval testRegistry = object : ActivityResultRegistry() {override fun <I, O> onLaunch(requestCode: Int,contract: ActivityResultContract<I, O>,input: I,options: ActivityOptionsCompat?) {dispatchResult(requestCode, expectedResult)}}// Use the launchFragmentInContainer method that takes a// lambda to construct the Fragment with the testRegistrywith(launchFragmentInContainer { MyFragment(testRegistry) }) {onFragment { fragment ->// Trigger the ActivityResultLauncherfragment.takePicture()// Verify the result is setassertThat(fragment.thumbnailLiveData.value).isSameInstanceAs(expectedResult)}}
}

创建自定义协定

虽然 ActivityResultContracts 包含一些预先构建的可用 ActivityResultContract 类,但您可以使用自己的协定,提供您所需要的精确类型安全 API。

每个 ActivityResultContract 都需要定义输入和输出类,如果您不需要任何输入,可使用 Void(在 Kotlin 中,使用 Void? 或 Unit)作为输入类型。

每个协定都必须实现 createIntent() 方法,该方法接受 Context 和输入内容作为参数,并构造将与 startActivityForResult() 配合使用的 Intent

每个协定还必须实现 parseResult(),这会根据指定的 resultCode(如 Activity.RESULT_OK 或 Activity.RESULT_CANCELED)和 Intent 生成输出内容。

如果无需调用 createIntent()、启动另一个 activity 并借助 parseResult() 来构建结果即可确定指定输入内容的结果,协定可以选择性地实现 getSynchronousResult()。

class PickRingtone : ActivityResultContract<Int, Uri?>() {     override fun createIntent(context: Context, ringtoneType: Int) =         Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply {             putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, ringtoneType)         }     override fun parseResult(resultCode: Int, result: Intent?) : Uri? {         if (resultCode != Activity.RESULT_OK) {             return null         }         return result?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)     } }

如果您不需要自定义协定,则可以使用 StartActivityForResult 协定。这是一个通用协定,它可接受任何 Intent 作为输入内容并返回 ActivityResult,让您能够在回调中提取 resultCode 和 Intent,如以下示例所示:

val startForResult = registerForActivityResult(StartActivityForResult()) { result: ActivityResult ->     if (result.resultCode == Activity.RESULT_OK) {         val intent = result.data         // Handle the Intent     } } override fun onCreate(savedInstanceState: Bundle) {     // ...     val startButton = findViewById(R.id.start_button)     startButton.setOnClickListener {         // Use the Kotlin extension in activity-ktx         // passing it the Intent you want to start         startForResult.launch(Intent(this, ResultProducingActivity::class.java))     } }

相关文章:

registerForActivityResult使用

目录 针对 activity 结果注册回调 启动 activity 以获取其结果 在单独的类中接收 activity 结果 测试 创建自定义协定 registerForActivityResult()是startActivityForResult&#xff08;&#xff09;的替代&#xff0c;简化了数据回调的写法 启动另一个 activity&#x…...

工作中,python真的有用吗?

普通上班族学Python有用吗&#xff1f; 那么&#xff0c;我也在这里提出一个问题&#xff1a;Python究竟适不适合办公人士来学习&#xff0c;以及学了之后究竟能不能给我的工作来带质一般的飞跃&#xff1f; 以我的亲身经历为例&#xff0c;我可以很负责的告诉大家&#xff0c…...

固态继电器控制电路

固态继电器控制电路 固态继电器&#xff08;SSR&#xff09;的种类和型号很多&#xff0c;因此其输入控制方法和控制电路也相应众多。固态继电器&#xff08;SSR&#xff09;的共同特点在于驱动电流或驱动电压小&#xff0c;即只需输入一个小信号即可控制SSR的开关。 如果需要…...

数仓、数据湖、湖仓一体、数据网格的探索与研究

第一代&#xff1a;数据仓库 定义 为解决数据库面对数据分析的不足&#xff0c;孕育出新一类产品数据仓库。数据仓库&#xff08;Data Warehouse&#xff09;是一个面向主题的、集成的、相对稳定的、反映历史变化的数据集合&#xff0c;用于支持管理决策和信息的全局共享。 数…...

设计模式系列 - 备忘录模式

介绍&定义 备忘录模式&#xff0c;也叫快照&#xff08;Snapshot&#xff09;模式&#xff0c;英文翻译是 Memento Design Pattern。在 GoF 的《设计模式》一书中&#xff0c;备忘录模式是这么定义的&#xff1a; Captures and externalizes an object’s internal state…...

详细介绍React生命周期和diffing算法

事件处理 1.通过onXxx属性指定事件处理函数(注意大小写) React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —— 为了更好的兼容性&#xff1b;React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ——为了的高效。 2.通过event.target得到发生事件的DOM…...

面向对象的特点

1、什么是对象对象的含义是指具体的某一个事物&#xff0c;即在现实生活中能够看得见摸得着的事物。在面向对象程序设计中&#xff0c;对象所指的是计算机系统中的某一个成分。在面向对象程序设计中&#xff0c;对象包含两个含义&#xff0c;其中一个是数据&#xff0c;另外一个…...

智慧校园平台源码 智慧教务 智慧电子班牌系统

系统介绍 智慧校园系统是通过信息化手段,实现对校园内各类资源的有效集成 整合和优化&#xff0c;实现资源的有效配置和充分利用&#xff0c;将校务管理过程的优化协调。为校园提供数字化教学、数字化学习、数字化科研和数字化管理。 致力于为家长和教师提供一个全方位、多层…...

Vue篇.03-组合式API [setup()]

单文件组件(1)<script setup><script setup> 是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。当同时使用 SFC 与组合式 API 时该语法是默认推荐启用该语法&#xff0c;需要在 <script> 代码块上添加 setup attribute, 里面的代码会被编译成组件 s…...

QHashIterator-官翻

QHashIterator Class template <typename Key, typename T> class QHashIterator QHashIterator 类为 QHash 和 QMultiHash 提供 Java 风格的常量迭代器。更多内容… 头文件:#include qmake:QT core 所有成员列表&#xff0c;包括继承的成员废弃的成员 公共成员函数…...

[qiankun]-部署后线上问题

[qiankun]-部署后线上问题微服务加载问题-现象1现象描述问题分析解决方案微服务加载问题-现象2现象描述问题分析微服务加载问题-现象3现象描述分析解决方案属于项目打包后&#xff0c;部署到服务器上&#xff0c;所遇到的部分问题 微服务加载问题-现象1 现象描述 项目部署实…...

位图数组 布隆过滤器

文章目录位图数组获取索引获取索引状态设置索引状态布隆过滤器特点大致原理位图数组 一个int类型的整数用4字节,也就是32个bit位来表示&#xff0c;将整数类型的数组转换成位图数组&#xff0c;那么存储长度将变为原来的32倍 arr[0] 表示0-31 arr[1] 表示32-63 //...获取索引…...

多线程Thread常用方法和状态

Thread类 及常见方法 1、常见构造方法 方法说明Thread()创建线程对象Thread(Runnable target)使用 Runnable 对象创建线程对象Thread(String name)创建线程对象&#xff0c;并命名Thread(Runnable target, String name)使用 Runnable 对象创建线程对象&#xff0c;并命名Thre…...

Codeforces Round #836 (Div. 2)

A SSeeeeiinngg DDoouubbllee 题意&#xff1a;告诉你一个字符串。若该串上每一位上的字母都可以出现两次&#xff0c;求回文串 思路&#xff1a;正向再反向输出s即可 #include <bits/stdc.h> #define lowbit(x) x&(-x) #define ios cin.sync_with_stdio(false)…...

Python学习之项目实践: 写一个MP3播放器

下面呢&#xff0c;是一个 Python MP3 播放器&#xff0c;它使用 pygame 模块来实现音乐播放功能&#xff1a; import pygame class MP3Player: """ MP3 播放器类 """ def __init__(self): pygame.mixer.init() def play(self, file_path): &quo…...

RocketMQTemplate 实现消息发送

代码托管于gitee&#xff1a;easy-rocketmq 文章目录一、前置工作二、消费者三、生产者1. 普通消息2. 过滤消息3. 同步消息4. 延时消息5. 批量消息6. 异步消息7. 单向消息8. 顺序消息9. 事务消息概要Demo源码解读一、前置工作 1、导入依赖 <dependency><groupId>…...

教师干货丨这5款微课必备提效神器,我要告诉全世界!

微课是一种短小精悍的视频教学形式&#xff0c;其设计和演示因特别简洁明了而被定义为“小而美”。由于只在几分钟时间内向学生传授所需知识&#xff0c;微课为学习者提供更多的选择机会和时间节约的便利&#xff0c;而这种趋势已经逐渐在新的社交媒体环境中显现出来。在制作微…...

timm使用swin-transformer

1.安装 pip install timm2.timm中有多少个预训练模型 #timm中有多少个预训练模型 model_pretrain_list timm.list_models(pretrainedTrue) print(len(model_pretrain_list), model_pretrain_list[:3])3加载swin模型一般准会出错 model_ft timm.create_model(swin_base_pat…...

【java基础】java八大基本数据类型和运算符

文章目录说明八大基本数据类型整型浮点型字符型布尔类型类型转换java运算符基础运算符二元运算符自增自减运算符关系和boolean运算符三元运算符位运算符运算符优先级说明 这里介绍java的八大基本数据类型和运算符 八大基本数据类型 java中有八大数据类型&#xff0c;4个整型…...

Mybatis源码学习笔记(四)之Mybatis执行增删改查方法的流程解析

1 Mybatis流程解析概述 Mybatis框架在执行增伤改的流程基本相同&#xff0c; 很简单&#xff0c;这个大家只要自己写个测试demo跟一下源码,基本就能明白是怎么回事&#xff0c;查询操作略有不同&#xff0c; 这里主要通过查询操作来解析一下整个框架的流程设计实现。 2 Mybat…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

均衡后的SNRSINR

本文主要摘自参考文献中的前两篇&#xff0c;相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程&#xff0c;其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt​ 根发送天线&#xff0c; n r n_r nr​ 根接收天线的 MIMO 系…...

LLMs 系列实操科普(1)

写在前面&#xff1a; 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容&#xff0c;原视频时长 ~130 分钟&#xff0c;以实操演示主流的一些 LLMs 的使用&#xff0c;由于涉及到实操&#xff0c;实际上并不适合以文字整理&#xff0c;但还是决定尽量整理一份笔…...

关于uniapp展示PDF的解决方案

在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项&#xff1a; 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库&#xff1a; npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...

探索Selenium:自动化测试的神奇钥匙

目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...

【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅

目录 前言 操作系统与驱动程序 是什么&#xff0c;为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中&#xff0c;我们在使用电子设备时&#xff0c;我们所输入执行的每一条指令最终大多都会作用到硬件上&#xff0c;比如下载一款软件最终会下载到硬盘上&am…...

Leetcode33( 搜索旋转排序数组)

题目表述 整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&#xff09;上进行了 旋转&#xff0c;使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...

redis和redission的区别

Redis 和 Redisson 是两个密切相关但又本质不同的技术&#xff0c;它们扮演着完全不同的角色&#xff1a; Redis: 内存数据库/数据结构存储 本质&#xff1a; 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能&#xff1a; 提供丰…...

【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权

摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题&#xff1a;安全。文章将详细阐述认证&#xff08;Authentication) 与授权&#xff08;Authorization的核心概念&#xff0c;对比传统 Session-Cookie 与现代 JWT&#xff08;JS…...