At this time you all probably heard about the new API to get the result from an Activity. In a nutshell, they look something like this:
目前,大家可能都听说过有关从活动获取结果的新API。 简而言之,它们看起来像这样:
val getContent = registerForActivityResult(GetContent()) {
// Handle the returned Uri
}
override fun onCreate(savedInstanceState: Bundle?) {
// ...
selectButton.setOnClickListener {
getContent.launch("image/*")
}
}
They change from hooking into onActivityResult
to a more elegant callback style. And as always, it comes with some gotcha as mentioned here.
它们从挂钩到onActivityResult
变为更优雅的回调样式。 和往常一样,它带有此处提到的一些陷阱。
If you use the Navigation Component and want to get a result from another Fragment
, then good luck, because you have to do some crazy stuff like:
如果您使用导航组件并希望从另一个Fragment
获得结果,那么祝您好运,因为您必须做一些疯狂的事情,例如:
findNavController().currentBackStackEntry
?.savedStateHandle?.set("a", "b")findNavController().previousBackStackEntry
?.savedStateHandle
?.getLiveData<String>("a")
?.observe(viewLifecycleOwner) {
// handle the result
}
And it with some gotcha as usual.
和往常一样有些陷阱。
At this point, a thought comes through my mind: “Heck, why so complicated” and decide to solve this problem my way. The way that is simple, easy to use, and doesn't get deprecated in a few years.
这时,我想到一个想法:“哎呀,为什么这么复杂”,并决定以我的方式解决这个问题。 这种简单,易用且在几年内不会弃用的方式。
After some experiment, I come up with something called ResultManager
经过一些实验,我想出了一个叫做ResultManager
东西
@Singleton
class ResultManager @Inject constructor() {
private val requests = HashMap<String, CancellableContinuation<*>>()
suspend fun <T> getResult(requestCode: String): T = suspendCancellableCoroutine { continuation ->
requests[requestCode] = continuation
continuation.invokeOnCancellation {
requests.remove(requestCode)
}
}
fun <T> sendResult(requestCode: String, result: T) {
getRequest<T>(requestCode)?.resume(result)
requests.remove(requestCode)
}
fun cancel(requestCode: String) {
val request = getRequest<Any>(requestCode)
request?.cancel()
requests.remove(requestCode)
}
private fun <T> getRequest(requestCode: String): CancellableContinuation<T>? {
val request = requests[requestCode] ?: return null
@Suppress("UNCHECKED_CAST")
return request as? CancellableContinuation<T>
}
}
The basic idea is every time you want to get a result from Activity/Fragment. First, generate a request code (a UUID will do), call getResult immediately (best to put the call inside ViewModel). Then launch the Activity/Fragment with the request code as a parameter.
基本思想是每次您想要从“活动/片段”获得结果时。 首先,生成一个请求代码(一个UUID会做),立即调用getResult(最好将调用放入ViewModel中)。 然后以请求代码为参数启动活动/片段。
// in ViewModel
viewModelScope.launch(Dispatchers.IO) {
val result = resultManager.getResult<FormatOption>(requestCode)
// process result
}
// in Activity/Fragment/View
val args = SelectFormatFragmentArgs(UUID.randomUUID().toString())
findNavController().navigate(R.id.select_format, args.toBundle())
When you want to send back the result, just call sendResult
当您想发回结果时,只需调用sendResult
// in where ever you want
resultManager.sendResult(args.requestCode, result)
controller.popBackStack()
// in case you don't want to send back the result
// cancel the request for result
override fun onCleared() {
super.onCleared()
resultManager.cancel(requestCode = args.requestCode)
}
You don’t need to call cancel, everything will still work because of the call resultManager.getResult
get suspended until someone sent back the result until then nothing will happen, inside onClear
the viewModelScope
got canceled so that the request coroutine got canceled too.
您不需要调用cancel,由于调用resultManager.getResult
被挂起,直到有人将结果发送回去,直到一切都没有发生之前,一切仍将正常进行,在onClear
, viewModelScope
被取消,因此协程请求也被取消了。
And just like that, you have a simple homemade solution to deal with the “result” problem. The best thing about this approach:
就像那样,您有一个简单的自制解决方案来解决“结果”问题。 关于这种方法的最好的事情是:
- The ResultManager the platform-neutral, so you can do flexible stuff like an Activity request result from a Fragment, a View, or literally anything. You can also request a result, then send the result back from 2–3 aways in the navigation stack, as long as you have the request code, you are good to go. ResultManager与平台无关,因此您可以执行灵活的操作,例如来自Fragment,View或其他任何内容的Activity请求结果。 您还可以请求一个结果,然后将结果从导航堆栈中的2-3个距离发送回去,只要您有请求代码,就可以了。
The Result’s type can be anything, not limited to
Serialiable
orParcelable
结果的类型可以是任何东西,不局限于
Serialiable
或Parcelable
永不弃用:) (Never deprecated : ))
翻译自: https://medium.com/@nlg.tuan.kiet/the-story-about-startactivityforresult-in-android-27b8767a718f