② Fragment
同样跟下 registerForActivityResult()
:
最终调用 prepareCallInternal()
:
思路也很简单,想办法拿到 宿主Activity中的ActivityResultRegistry实例
,调它的 register()
拿到返回的 ActivityResultLauncher实例引用
。最后返回 新的ActivityResultLauncher 实例,在launch()中调用前面那个Activity的 ActivityResultLauncher实例引用
的launch()方法。TM调的是Activity的launch(),这一手 委托代理 玩挺6啊。
对了,这有个小细节,生命周期组件传入的是 Fragment.this,所以不用担心Fragment销毁没解绑导致的内存泄露问题。
③ 非Activity/Fragment 接收Activity结果
实现一个 LifecycleObserver
用于处理协定的注册和启动器的启动,代码示例如下:
调用处:
④ 亿点小细节:配置改变引起Activity重建的处理
在 ActivityResultRegistry
中还发现了介个:
好家伙,连配置更改导致重建的场景也考虑到了吗?
保存了:key(requestCode)相关的数据、处理结果、Random随机数实例。
requestCode和Result得以保留,Activity重建后,再把它们分发给新注册的Callback,避免了数据的丢失。
⑤ 亿点补充:测试Activity结果调用
默认情况下,registerForActivityResult()
会自动使用Activity提供的 ActivityResultRegistry
,而它还提供了一个重载,支持传入自己的 ActivityResultRegistry
实例。能干嘛?拦截结果调用进行测试啊,不会另外启动另一个Activity。代码示例如下:
0x3、关于封装
Activity Results API 了解得七七八八了,接下来可以放心地用到项目中了,虽然它的API已经很简单易用了。但对于 喜欢偷懒到极致的开发仔 来说还是不够的,可以利用Kotlin相关的语法特性,封装下再少写一些代码。
捋下API使用链条:
registerForActivityResult()
→ActivityResultLauncher
,需在ON_START或之前注册,在OnCreate()时再初始化会报错,还得传入一个**ActivityResultContract
** 实例,最后跟一个ActivityResultCallback
回调。- 调用
ActivityResultLauncher#launch()
才触发页面跳转,需要传入一个输入(如Intent)实例。
最简单的封装就是写几个 扩展方法,从ActivityResultLauncher生成和launch()调用处入手:
// 扩展 fun ComponentActivity.registerActResult(callback: ActivityResultCallback<ActivityResult>) = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { callback.onActivityResult(it) } fun Fragment.registerActResult(callback: ActivityResultCallback<ActivityResult>) = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { callback.onActivityResult(it) } fun Intent.launch(launcher: ActivityResultLauncher<Intent>) { launcher.launch(this) } // 注册处 private val mLauncher = registerActResult { shortToast("收到测试数据:${it.data?.getStringExtra("value")}") } // 调用处 Intent(this, SecondActivity::class.java).launch(mLauncher)
还可以在优化下,比如改成基于 ActivityResultCaller
进行扩展,然后把常用的一些跳转,如权限、打开相机、录像等写成一个个扩展函数,用的时候直接调用即可。懒得自己写或者想找参考的可以看看 → ActivityResult.kt
如果想代码写得更少更优雅,可以折腾得更复杂些,比如结合生命周期回调,各种简化调用的扩展,甚至弄成DSL调用等,具体可以参考这些:
怎么封装看自己,觉得适合就行,笔者就懒得整那么复杂了~
0x4、小结
借着重构BaseFragment的机缘巧合,过了波Activity Results API的用法,阅读源码了解到背后的实现原理,小试了一下封装。心里有底了,赶紧在重构项目的时候安排上!!!
参考文献:
- 官方文档:获取 activity 的结果
- 优雅地封装 Activity Result API,完美地替代 startActivityForResult()
- Android onActivityResult的替代方法—registerForActivityResult