当前位置: 首页 > 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…...

浅谈测试用例设计

前言 最近干的最多的事情就是设计测试用例、评审测试用例了&#xff0c;于是我不禁又想到了一个经典的问题&#xff1a;如何设计出优秀的测试用例&#xff1f; 可能有些童鞋看到这个问题会有些不以为然&#xff0c;这有什么好想的&#xff1f;干个测试谁还不会设计测试用例&a…...

python 利用装饰器实现类似于flask路由

例子1&#xff1a; def f1():print(1111)def f2():print(2222)if __name__ __main__:print(33)打印结果&#xff1a; 33 在例子1中&#xff0c;f1() 与f2() 都没有被调用&#xff0c;只执行了print(33) f1与f2&#xff0c;是没有被调用的&#xff0c;但是如果f1 和 f2 上面…...

git 拉取远程分支到本地

目录&#xff1a;***&#xff01;本小作者&#xff0c;是将终端和Git的可视化插件结合使用&#xff0c;刚接触的可以自习看一下&#xff0c;内容简单&#xff0c;避免弯路&#xff01;***一&#xff0c;简单了解远程分支1&#xff0c;连接远程&#xff1a;2&#xff0c;提交&am…...

Answering Multi-Dimensional Range Queries under Local Differential Privacy

文章目录AbstractIntroduction2 PRELIMINARIES2.12.2 Categorical Frequency Oracles4 GRID APPROACHES4.1概述Abstract 在本文中&#xff0c;我们解决了在局部差异隐私下回答多维范围查询的问题。有三个关键的技术挑战&#xff1a;捕捉属性之间的相关性&#xff0c;避免维度的…...

手把手搭建springboot项目05-springboot整合Redis及其业务场景

目录前言一、食用步骤1.1 安装步骤1.1.1 客户端安装1.2 添加依赖1.3 修改配置1.4 项目使用1.5 序列化二、应用场景2.1 缓存2.2.分布式锁2.2.1 redis实现2.2.2 使用Redisson 作为分布式锁2.3 全局ID、计数器、限流2.4 购物车2.5 消息队列 (List)2.6 点赞、签到、打卡 (Set)2.7 筛…...

Flutter基础语法(六)var、final、const、late

Flutter基础 第六章 Flutter关键字var、final、const、late的区别与使用 文章目录Flutter基础前言一、var1.var是什么?2.var如何使用3.var自动推断类型4.var可以再次赋值5.var指定类型二、final1.final是什么?2.final声明但不赋值3.final赋值多次4.final正常使用三、const1.…...

Linux之安装node

Linux之安装node步骤如下 1.去网站下载node 下载地址&#xff1a; https://npm.taobao.org/mirrors/ 2.上传到指定目录下 3.解压 tar -zxvf node-v17.3.0-linux-x644.配置node环境变量 //执行以下命令 vim /etc/profile //在path中加入以下内容 /usr/local/node-v15.14.0/b…...

二叉树、二叉搜索树、二叉树的最近祖先、二叉树的层序遍历【零神基础精讲】

来源0x3f&#xff1a;https://space.bilibili.com/206214 文章目录二叉树[104. 二叉树的最大深度](https://leetcode.cn/problems/maximum-depth-of-binary-tree/)[111. 二叉树的最小深度](https://leetcode.cn/problems/minimum-depth-of-binary-tree/)[129. 求根节点到叶节点…...

【算法】【数组与矩阵模块】求最长可整合子数组和子数组的长度

目录前言问题介绍解决方案代码编写java语言版本c语言版本c语言版本思考感悟写在最后前言 当前所有算法都使用测试用例运行过&#xff0c;但是不保证100%的测试用例&#xff0c;如果存在问题务必联系批评指正~ 在此感谢左大神让我对算法有了新的感悟认识&#xff01; 问题介绍 …...

数据结构:循环队列的实现(leetcode622.设计循环队列)

目录 一.循环队列简单介绍 二.用静态数组实现循环队列 1.数组循环队列结构设计 2.数组循环队列的堆区内存申请接口 3.数据出队和入队的接口实现 4.其他操作接口 5.数组循环队列的实现代码总览 三.静态单向循环链表实现循环队列 1.链表循环队列的结构设计 2.创建静…...