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()的替代,简化了数据回调的写法 启动另一个 activity&#x…...
工作中,python真的有用吗?
普通上班族学Python有用吗? 那么,我也在这里提出一个问题:Python究竟适不适合办公人士来学习,以及学了之后究竟能不能给我的工作来带质一般的飞跃? 以我的亲身经历为例,我可以很负责的告诉大家,…...
固态继电器控制电路
固态继电器控制电路 固态继电器(SSR)的种类和型号很多,因此其输入控制方法和控制电路也相应众多。固态继电器(SSR)的共同特点在于驱动电流或驱动电压小,即只需输入一个小信号即可控制SSR的开关。 如果需要…...
数仓、数据湖、湖仓一体、数据网格的探索与研究
第一代:数据仓库 定义 为解决数据库面对数据分析的不足,孕育出新一类产品数据仓库。数据仓库(Data Warehouse)是一个面向主题的、集成的、相对稳定的、反映历史变化的数据集合,用于支持管理决策和信息的全局共享。 数…...
设计模式系列 - 备忘录模式
介绍&定义 备忘录模式,也叫快照(Snapshot)模式,英文翻译是 Memento Design Pattern。在 GoF 的《设计模式》一书中,备忘录模式是这么定义的: Captures and externalizes an object’s internal state…...
详细介绍React生命周期和diffing算法
事件处理 1.通过onXxx属性指定事件处理函数(注意大小写) React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —— 为了更好的兼容性;React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ——为了的高效。 2.通过event.target得到发生事件的DOM…...
面向对象的特点
1、什么是对象对象的含义是指具体的某一个事物,即在现实生活中能够看得见摸得着的事物。在面向对象程序设计中,对象所指的是计算机系统中的某一个成分。在面向对象程序设计中,对象包含两个含义,其中一个是数据,另外一个…...
智慧校园平台源码 智慧教务 智慧电子班牌系统
系统介绍 智慧校园系统是通过信息化手段,实现对校园内各类资源的有效集成 整合和优化,实现资源的有效配置和充分利用,将校务管理过程的优化协调。为校园提供数字化教学、数字化学习、数字化科研和数字化管理。 致力于为家长和教师提供一个全方位、多层…...
Vue篇.03-组合式API [setup()]
单文件组件(1)<script setup><script setup> 是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。当同时使用 SFC 与组合式 API 时该语法是默认推荐启用该语法,需要在 <script> 代码块上添加 setup attribute, 里面的代码会被编译成组件 s…...
QHashIterator-官翻
QHashIterator Class template <typename Key, typename T> class QHashIterator QHashIterator 类为 QHash 和 QMultiHash 提供 Java 风格的常量迭代器。更多内容… 头文件:#include qmake:QT core 所有成员列表,包括继承的成员废弃的成员 公共成员函数…...
[qiankun]-部署后线上问题
[qiankun]-部署后线上问题微服务加载问题-现象1现象描述问题分析解决方案微服务加载问题-现象2现象描述问题分析微服务加载问题-现象3现象描述分析解决方案属于项目打包后,部署到服务器上,所遇到的部分问题 微服务加载问题-现象1 现象描述 项目部署实…...
位图数组 布隆过滤器
文章目录位图数组获取索引获取索引状态设置索引状态布隆过滤器特点大致原理位图数组 一个int类型的整数用4字节,也就是32个bit位来表示,将整数类型的数组转换成位图数组,那么存储长度将变为原来的32倍 arr[0] 表示0-31 arr[1] 表示32-63 //...获取索引…...
多线程Thread常用方法和状态
Thread类 及常见方法 1、常见构造方法 方法说明Thread()创建线程对象Thread(Runnable target)使用 Runnable 对象创建线程对象Thread(String name)创建线程对象,并命名Thread(Runnable target, String name)使用 Runnable 对象创建线程对象,并命名Thre…...
Codeforces Round #836 (Div. 2)
A SSeeeeiinngg DDoouubbllee 题意:告诉你一个字符串。若该串上每一位上的字母都可以出现两次,求回文串 思路:正向再反向输出s即可 #include <bits/stdc.h> #define lowbit(x) x&(-x) #define ios cin.sync_with_stdio(false)…...
Python学习之项目实践: 写一个MP3播放器
下面呢,是一个 Python MP3 播放器,它使用 pygame 模块来实现音乐播放功能: import pygame class MP3Player: """ MP3 播放器类 """ def __init__(self): pygame.mixer.init() def play(self, file_path): &quo…...
RocketMQTemplate 实现消息发送
代码托管于gitee:easy-rocketmq 文章目录一、前置工作二、消费者三、生产者1. 普通消息2. 过滤消息3. 同步消息4. 延时消息5. 批量消息6. 异步消息7. 单向消息8. 顺序消息9. 事务消息概要Demo源码解读一、前置工作 1、导入依赖 <dependency><groupId>…...
教师干货丨这5款微课必备提效神器,我要告诉全世界!
微课是一种短小精悍的视频教学形式,其设计和演示因特别简洁明了而被定义为“小而美”。由于只在几分钟时间内向学生传授所需知识,微课为学习者提供更多的选择机会和时间节约的便利,而这种趋势已经逐渐在新的社交媒体环境中显现出来。在制作微…...
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中有八大数据类型,4个整型…...
Mybatis源码学习笔记(四)之Mybatis执行增删改查方法的流程解析
1 Mybatis流程解析概述 Mybatis框架在执行增伤改的流程基本相同, 很简单,这个大家只要自己写个测试demo跟一下源码,基本就能明白是怎么回事,查询操作略有不同, 这里主要通过查询操作来解析一下整个框架的流程设计实现。 2 Mybat…...
链表(两数相加)(1)
一.题目 2. 两数相加 - 力扣(LeetCode) 二.思路讲解 2.1 审题 题目给出两个非空链表,每个链表表示一个非负整数,并且数字是逆序存储的,即链表的头节点对应数字的最低位。例如,链表 2->4->3 表示数字…...
红外遥控技术原理与电路设计实践
1. 红外遥控技术概述红外遥控技术自20世纪70年代问世以来,已经成为家电控制领域最成熟、应用最广泛的无线控制方案。作为一名电子工程师,我在多个家电项目中都深度应用过这项技术。它的核心原理其实很简单:通过红外发光二极管(IRE…...
PTP协议避坑指南:1588v2时间同步测试中常见的5个错误及解决方法
PTP协议避坑指南:1588v2时间同步测试中常见的5个错误及解决方法 在工业自动化、电信网络和电力系统等对时间同步精度要求极高的领域,1588v2协议(Precision Time Protocol)已经成为实现微秒级甚至纳秒级时间同步的事实标准。然而&a…...
微信小程序uView框架下u-picker三级联动实战:从接口加载到视图强制更新
微信小程序uView框架下u-picker三级联动实战:从接口加载到视图强制更新 在微信小程序开发中,省市区三级联动选择器是常见的功能需求。uView作为一款优秀的小程序UI框架,其u-picker组件提供了强大的多级联动支持。本文将深入探讨如何通过接口异…...
OpenClaw安全实践:Phi-3-mini-128k-instruct本地化部署的3个关键配置
OpenClaw安全实践:Phi-3-mini-128k-instruct本地化部署的3个关键配置 1. 为什么需要关注OpenClaw的安全配置? 去年夏天,我在整理个人财务数据时突发奇想:能否用AI自动生成月度支出分析报告?这个看似简单的需求&#…...
002、现代Python后端开发环境与工具链搭建
002、现代Python后端开发环境与工具链搭建 上周排查一个线上问题,日志里报了个ImportError: cannot import name ... from partially initialized module。花了半小时才发现,是同事本地虚拟环境混用了Python 3.8和3.10的依赖,打包时没锁版本。…...
einops.reduce隐藏技巧:3行代码实现CNN池化层效果(对比MaxPool2d性能)
einops.reduce隐藏技巧:3行代码实现CNN池化层效果(对比MaxPool2d性能) 在计算机视觉模型的优化过程中,池化层一直扮演着至关重要的角色。传统的MaxPool2d虽然高效,但在某些场景下显得过于刚性。最近在重构一个轻量级图…...
从Revit/BIM到Cesium:CesiumLab 4.0.7插件全流程打通,属性信息一个不丢
从Revit到Cesium的无损数据迁移:CesiumLab 4.0.7全流程深度解析 1. BIM与三维GIS融合的技术演进 在建筑信息模型(BIM)与地理信息系统(GIS)的交叉领域,数据互操作性一直是行业痛点。传统工作流中,…...
利用快马AI平台,十分钟快速搭建SpringCloud微服务原型
利用快马AI平台,十分钟快速搭建SpringCloud微服务原型 最近在尝试搭建一个SpringCloud微服务项目原型,发现传统方式需要手动配置各种组件,耗时又容易出错。后来发现了InsCode(快马)平台,它通过AI智能生成能力,能快速搭…...
棒板电极流注放电与氩气等离子体仿真的COMSOL研究
棒板电极流注放电, COMSOL,氩气形成的贯穿流注 氩气放电等离子体仿真。在高压实验室里见过那种细金属棒和接地板之间突然爆发的紫色放电吗?那玩意儿专业名称叫棒板电极流注放电。今天咱们用COMSOL扒开这朵"电火花"的外衣࿰…...
