如何让已完成的CompletableDeferred恢复未完成状态?及替代方案咨询
问题解答
1. CompletableDeferred无法恢复为未完成状态
CompletableDeferred的设计本质就是一次性完成的,一旦调用complete()、completeExceptionally()或者被取消,它的状态就永久固定了,没有任何官方API能把它重置回未完成状态。所以你没法通过单个CompletableDeferred实现“每次重试都等待用户点击”的需求。
2. 更合适的替代方案
方案一:使用Channel(推荐)
Channel支持多次发送信号,每次重试时从Channel中获取新的触发信号,完美匹配“每次异常都等用户点击再重试”的场景。修改后的代码如下:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // CONFLATED模式避免重复点击导致信号堆积,只保留最新的一次点击信号 val retryChannel = Channel<Unit>(Channel.CONFLATED) lifecycleScope.launch { flow { for (x in 1..10) { if (x == 2) throw Throwable() emit(x) } }.retryWhen { _, _ -> Log.w("L384", "retryWhen") retryChannel.receive() // 阻塞等待用户点击的信号 true }.collect { Log.w("L384", "$it") } } val retryBtn: Button = findViewById(R.id.btn) retryBtn.setOnClickListener { lifecycleScope.launch { retryChannel.send(Unit) // 点击时发送重试触发信号 } } }
方案二:使用MutableStateFlow
通过StateFlow维护一个可重置的触发状态,每次点击切换状态,重试后再重置状态:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val retryTrigger = MutableStateFlow(false) lifecycleScope.launch { flow { for (x in 1..10) { if (x == 2) throw Throwable() emit(x) } }.retryWhen { _, _ -> Log.w("L384", "retryWhen") retryTrigger.first { it } // 等待状态变为true(用户点击) retryTrigger.value = false // 重置状态,确保下次重试需要再次点击 true }.collect { Log.w("L384", "$it") } } val retryBtn: Button = findViewById(R.id.btn) retryBtn.setOnClickListener { retryTrigger.value = true } }
方案三:每次重试创建新的CompletableDeferred
如果一定要用CompletableDeferred,可以在retryWhen中每次创建新的实例,替代复用同一个对象:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var currentRetrySignal: CompletableDeferred<Unit>? = null lifecycleScope.launch { flow { for (x in 1..10) { if (x == 2) throw Throwable() emit(x) } }.retryWhen { _, _ -> Log.w("L384", "retryWhen") currentRetrySignal = CompletableDeferred() // 每次重试创建新的信号 currentRetrySignal?.await() true }.collect { Log.w("L384", "$it") } } val retryBtn: Button = findViewById(R.id.btn) retryBtn.setOnClickListener { currentRetrySignal?.complete(Unit) // 触发当前的重试信号 } }
内容的提问来源于stack exchange,提问作者Taha Sami
相关产品推荐
相关产品推荐

