关于Android中的startActivityForResult的故事

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被挂起,直到有人将结果发送回去,直到一切都没有发生之前,一切仍将正常进行,在onClearviewModelScope被取消,因此协程请求也被取消了。

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 or Parcelable

    结果的类型可以是任何东西,不局限于SerialiableParcelable

永不弃用:) (Never deprecated : ))

翻译自: https://medium.com/@nlg.tuan.kiet/the-story-about-startactivityforresult-in-android-27b8767a718f

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值