【问题标题】:Fragment popbackstack trigger lifecyclescope collect片段 popbackstack 触发生命周期范围收集
【发布时间】:2021-07-09 10:40:06
【问题描述】:

情况

我提交数据setTripDeliver,收集工作正常(触发加载,然后成功)。我按下一个按钮转到下一个片段 B(使用replace)。之后,我按下返回按钮(使用popbackstack)。触发收集成功。

代码相关

这些代码位于FragmentA.ktonViewCreated

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

问题

如何让它只触发一次?是我实现它的方式不对吗?提前谢谢你。

【问题讨论】:

    标签: android kotlin android-fragments fragment-lifecycle


    【解决方案1】:

    这不是您的实施的问题,这是因为 stateIn() 在您的viewModel 中使用将常规流转换为stateFlow
    如果根据您的代码 sn-p 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.

    【讨论】:

    • @TicherhazFreePalestine 你可以使用事件包装类
    【解决方案2】:

    我认为这是因为coldFlow,你需要一个HotFlow。另一种选择是尝试隐藏和显示片段,而不是替换。另一个解决方案是将此代码保留在 viewModel 中。

    【讨论】:

      【解决方案3】:

      我找到了解决方案,感谢@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))
              }
          }
      }
      

      我希望这个答案也对其他人有所帮助。

      【讨论】:

        【解决方案4】:

        在我看来,我认为您在 lifeScope 中使用协程的方式是不正确的。 FragmentA的lifeScope状态再次为Started后,协程会重启:

        launch {
           collectTripDeliver()
        }
        launch {
           collectTripReattempt()
        }
        

        所以我认为:你需要这样修改:

        
        private fun startLifeCycle() {
             viewLifecycleOwner.lifecycleScope.launch {
                launch {
                   collectTripDeliver()
                }
                launch {
                   collectTripReattempt()
                }
             }
        }
        

        【讨论】:

        • 我认为launch的用法不能用于flow
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多