Fragment popbackstack trigger lifecyclescope collect
Fragment popbackstack trigger lifecyclescope collect
情况
我提交数据 setTripDeliver
,收集工作正常(触发加载然后成功)。我按下一个按钮转到下一个片段 B(使用 replace
)。之后,我按下后退按钮(使用 popbackstack
)。触发收集成功。
代码相关
这些代码在FragmentA.kt
里面onViewCreated
.
private fun startLifeCycle() {
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
collectTripDeliver()
}
launch {
collectTripReattempt()
}
}
}
}
这些代码何时通过按钮提交数据setOnClickListener
。
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
viewModel.setTripDeliver(
verificationCode,
remark
)
}
收集流量的方法collectTripReattempt()
private suspend fun collectTripReattempt() {
viewModel.tripReattempt.collect {
when (it) {
is Resource.Initialize -> {
}
is Resource.Loading -> {
Log.i("???","collectTripReattempt loading")
handleSaveEarly()
}
is Resource.Success -> {
val error = it.data?.error
if (error == null) {
Tools.showToast(requireContext(), "Success Reattempt")
Log.i("???","collectTripReattempt Success")
} else {
Tools.showToast(requireContext(), "$error")
}
handleSaveEnding()
}
is Resource.Error -> {
handleSaveEnding()
}
}
}
}
以下代码来自ViewModel
.
private val _tripDeliver =
MutableStateFlow<Resource<TripDeliverResponse>>(Resource.Initialize())
val tripDeliver: StateFlow<Resource<TripDeliverResponse>> = _tripDeliver
此方法调用存储库。
suspend fun setTripDeliver(
verificationCode: String?,
remark: String?
) {
_tripDeliver.value = Resource.Loading()
try {
val result = withContext(ioDispatcher) {
val tripDeliverParameter = DeliverParameter(
verificationCode,
remark
)
val response = appRepository.setTripDeliver(tripDeliverParameter)
Resource.getResponse { response }
}
_tripDeliver.value = result
} catch (e: Exception) {
when (e) {
is IOException -> _tripDeliver.value =
Resource.Error(messageInt = R.string.no_internet_connection)
else -> _tripDeliver.value =
Resource.Error("Trip Deliver Error: " + e.message)
}
}
}
Logcat
2021-07-09 19:56:10.946 7446-7446/com.package.app I/???: collectTripReattempt loading
2021-07-09 19:56:11.172 7446-7446/com.package.app I/???: collectTripReattempt Success
2021-07-09 19:56:17.703 7446-7446/com.package.app I/???: collectTripReattempt Success
如您所见,在我按下后退按钮 (popbackstack
)
后,最后一个 Success
被再次调用
问题
如何让它只触发一次?是我实现的方式不对吗?提前谢谢你。
这不是您实施的问题,这是因为 stateIn() 在您的 viewModel
中用于将常规流量转换为 stateFlow
如果根据您的代码片段 success 再次触发,那么为什么 loading 没有触发?
根据文章,它显示 latest cached value
当您离开屏幕并返回时,您会看到最新的缓存值。
资源:
https://medium.com/androiddevelopers/migrating-from-livedata-to-kotlins-flow-379292f419fb
The latest value will still be cached so that when the user comes back to it, the view will have some data immediately.
在我看来,我认为您在 lifeScope 中使用协程的方式是不正确的。 FragmentA的lifeScope状态再次为Started后,会重启协程:
launch {
collectTripDeliver()
}
launch {
collectTripReattempt()
}
所以我认为:你需要这样修改:
private fun startLifeCycle() {
viewLifecycleOwner.lifecycleScope.launch {
launch {
collectTripDeliver()
}
launch {
collectTripReattempt()
}
}
}
我认为这是因为coldFlow,你需要一个HotFlow。另一种选择是尝试隐藏和显示片段,而不是替换。另一种解决方案是将此代码保留在 viewModel 中。
我找到了解决方案,感谢@Nurseyit Tursunkulov 给我提供了线索。我必须使用 SharedFlow
.
在 ViewModel
,我将初始化替换为这些:
private val _tripDeliver = MutableSharedFlow<Resource<TripDeliverResponse>>(replay = 0)
val tripDeliver: SharedFlow<Resource<TripDeliverResponse>> = _tripDeliver
在replay
我必须使用0
,所以这个SharedFlow
会触发一次。接下来,将 _tripDeliver.value
更改为 _tripDeliver.emit()
,如下面的代码:
fun setTripDeliver(
verificationCode: String?,
remark: String?
) = viewModelScope.launch {
_tripDeliver.emit(Resource.Loading())
if (verificationCode == null && remark == null) {
_tripDeliver.emit(Resource.Error("Remark cannot be empty if verification is empty"))
return@launch
}
try {
val result = withContext(ioDispatcher) {
val tripDeliverParameter = DeliverParameter(
verificationCode,
remark,
)
val response = appRepository.setTripDeliver(tripDeliverParameter)
Resource.getResponse { response }
}
_tripDeliver.emit(result)
} catch (e: Exception) {
when (e) {
is IOException -> _tripDeliver.emit(Resource.Error(messageInt = R.string.no_internet_connection))
else -> _tripDeliver.emit(Resource.Error("Trip Deliver Error: " + e.message))
}
}
}
希望这个回答对其他人也有帮助。
情况
我提交数据 setTripDeliver
,收集工作正常(触发加载然后成功)。我按下一个按钮转到下一个片段 B(使用 replace
)。之后,我按下后退按钮(使用 popbackstack
)。触发收集成功。
代码相关
这些代码在FragmentA.kt
里面onViewCreated
.
private fun startLifeCycle() {
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
collectTripDeliver()
}
launch {
collectTripReattempt()
}
}
}
}
这些代码何时通过按钮提交数据setOnClickListener
。
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
viewModel.setTripDeliver(
verificationCode,
remark
)
}
收集流量的方法collectTripReattempt()
private suspend fun collectTripReattempt() {
viewModel.tripReattempt.collect {
when (it) {
is Resource.Initialize -> {
}
is Resource.Loading -> {
Log.i("???","collectTripReattempt loading")
handleSaveEarly()
}
is Resource.Success -> {
val error = it.data?.error
if (error == null) {
Tools.showToast(requireContext(), "Success Reattempt")
Log.i("???","collectTripReattempt Success")
} else {
Tools.showToast(requireContext(), "$error")
}
handleSaveEnding()
}
is Resource.Error -> {
handleSaveEnding()
}
}
}
}
以下代码来自ViewModel
.
private val _tripDeliver =
MutableStateFlow<Resource<TripDeliverResponse>>(Resource.Initialize())
val tripDeliver: StateFlow<Resource<TripDeliverResponse>> = _tripDeliver
此方法调用存储库。
suspend fun setTripDeliver(
verificationCode: String?,
remark: String?
) {
_tripDeliver.value = Resource.Loading()
try {
val result = withContext(ioDispatcher) {
val tripDeliverParameter = DeliverParameter(
verificationCode,
remark
)
val response = appRepository.setTripDeliver(tripDeliverParameter)
Resource.getResponse { response }
}
_tripDeliver.value = result
} catch (e: Exception) {
when (e) {
is IOException -> _tripDeliver.value =
Resource.Error(messageInt = R.string.no_internet_connection)
else -> _tripDeliver.value =
Resource.Error("Trip Deliver Error: " + e.message)
}
}
}
Logcat
2021-07-09 19:56:10.946 7446-7446/com.package.app I/???: collectTripReattempt loading
2021-07-09 19:56:11.172 7446-7446/com.package.app I/???: collectTripReattempt Success
2021-07-09 19:56:17.703 7446-7446/com.package.app I/???: collectTripReattempt Success
如您所见,在我按下后退按钮 (popbackstack
)
Success
被再次调用
问题
如何让它只触发一次?是我实现的方式不对吗?提前谢谢你。
这不是您实施的问题,这是因为 stateIn() 在您的 viewModel
中用于将常规流量转换为 stateFlow
如果根据您的代码片段 success 再次触发,那么为什么 loading 没有触发?
根据文章,它显示 latest cached value
当您离开屏幕并返回时,您会看到最新的缓存值。
资源:
https://medium.com/androiddevelopers/migrating-from-livedata-to-kotlins-flow-379292f419fb
The latest value will still be cached so that when the user comes back to it, the view will have some data immediately.
在我看来,我认为您在 lifeScope 中使用协程的方式是不正确的。 FragmentA的lifeScope状态再次为Started后,会重启协程:
launch {
collectTripDeliver()
}
launch {
collectTripReattempt()
}
所以我认为:你需要这样修改:
private fun startLifeCycle() {
viewLifecycleOwner.lifecycleScope.launch {
launch {
collectTripDeliver()
}
launch {
collectTripReattempt()
}
}
}
我认为这是因为coldFlow,你需要一个HotFlow。另一种选择是尝试隐藏和显示片段,而不是替换。另一种解决方案是将此代码保留在 viewModel 中。
我找到了解决方案,感谢@Nurseyit Tursunkulov 给我提供了线索。我必须使用 SharedFlow
.
在 ViewModel
,我将初始化替换为这些:
private val _tripDeliver = MutableSharedFlow<Resource<TripDeliverResponse>>(replay = 0)
val tripDeliver: SharedFlow<Resource<TripDeliverResponse>> = _tripDeliver
在replay
我必须使用0
,所以这个SharedFlow
会触发一次。接下来,将 _tripDeliver.value
更改为 _tripDeliver.emit()
,如下面的代码:
fun setTripDeliver(
verificationCode: String?,
remark: String?
) = viewModelScope.launch {
_tripDeliver.emit(Resource.Loading())
if (verificationCode == null && remark == null) {
_tripDeliver.emit(Resource.Error("Remark cannot be empty if verification is empty"))
return@launch
}
try {
val result = withContext(ioDispatcher) {
val tripDeliverParameter = DeliverParameter(
verificationCode,
remark,
)
val response = appRepository.setTripDeliver(tripDeliverParameter)
Resource.getResponse { response }
}
_tripDeliver.emit(result)
} catch (e: Exception) {
when (e) {
is IOException -> _tripDeliver.emit(Resource.Error(messageInt = R.string.no_internet_connection))
else -> _tripDeliver.emit(Resource.Error("Trip Deliver Error: " + e.message))
}
}
}
希望这个回答对其他人也有帮助。