Activity Result 有两个 API 界面:
Composable 与 Activity 或 Fragment 具有不同的生命周期(例如,如果您从层次结构中删除 Composable,它应该自行清理),因此使用ActivityResultCaller诸如此类的 APIregisterForActivityResult()绝不是正确的做法。
相反,您应该直接使用ActivityResultRegistryAPI,直接register()调用unregister()。rememberUpdatedState()这最好与和配对,DisposableEffect以创建一个registerForActivityResult与 Composable 一起使用的版本:
@Composable
fun <I, O> registerForActivityResult(
contract: ActivityResultContract<I, O>,
onResult: (O) -> Unit
) : ActivityResultLauncher<I> {
// First, find the ActivityResultRegistry by casting the Context
// (which is actually a ComponentActivity) to ActivityResultRegistryOwner
val owner = ContextAmbient.current as ActivityResultRegistryOwner
val activityResultRegistry = owner.activityResultRegistry
// Keep track of the current onResult listener
val currentOnResult = rememberUpdatedState(onResult)
// It doesn't really matter what the key is, just that it is unique
// and consistent across configuration changes
val key = rememberSavedInstanceState { UUID.randomUUID().toString() }
// Since we don't have a reference to the real ActivityResultLauncher
// until we register(), we build a layer of indirection so we can
// immediately return an ActivityResultLauncher
// (this is the same approach that Fragment.registerForActivityResult uses)
val realLauncher = mutableStateOf<ActivityResultLauncher<I>?>(null)
val returnedLauncher = remember {
object : ActivityResultLauncher<I>() {
override fun launch(input: I, options: ActivityOptionsCompat?) {
realLauncher.value?.launch(input, options)
}
override fun unregister() {
realLauncher.value?.unregister()
}
override fun getContract() = contract
}
}
// DisposableEffect ensures that we only register once
// and that we unregister when the composable is disposed
DisposableEffect(activityResultRegistry, key, contract) {
realLauncher.value = activityResultRegistry.register(key, contract) {
currentOnResult.value(it)
}
onDispose {
realLauncher.value?.unregister()
}
}
return returnedLauncher
}
然后可以通过以下代码在您自己的 Composable 中使用它:
val result = remember { mutableStateOf<Bitmap?>(null) }
val launcher = registerForActivityResult(ActivityResultContracts.TakePicturePreview()) {
// Here we just update the state, but you could imagine
// pre-processing the result, or updating a MutableSharedFlow that
// your composable collects
result.value = it
}
// Now your onClick listener can call launch()
Button(onClick = { launcher.launch() } ) {
Text(text = "Take a picture")
}
// And you can use the result once it becomes available
result.value?.let { image ->
Image(image.asImageAsset(),
modifier = Modifier.fillMaxWidth())
}