StateFlow:收集后取消旧的发射状态

Posted

技术标签:

【中文标题】StateFlow:收集后取消旧的发射状态【英文标题】:StateFlow: Cancellation of Older Emitted State After Collecting 【发布时间】:2022-01-18 09:57:57 【问题描述】:

我在 kotlin 流程中相对较新,我正在使用 android 中的流程创建登录模块。过去几天,我在 ViewModels 中收集它时一直被困在流程中,但是当我使用错误的凭据请求其缓存所有状态时,我遇到了问题。输入正确的凭据后,用户导航到主 Activity,但正在使用每个发出的状态创建 MainActivity 的实例:示例(用户输入 3 个错误的凭据和 1 个正确的凭据:创建了 4 个 MainActivity 实例) .那么,有什么方法可以取消之前的发射并只显示最新的请求。我也在使用 collectLatest ,但它也不起作用。下面是代码。

登录活动

override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(mViewBinding.root)

        loginListener()
        

    

    
    override fun onStart() 
        super.onStart()
        initViews()
        handleNetworkChanges()
    

    private fun observeLogin() 
        lifecycleScope.launchWhenCreated 
          mViewModel.loginCredentials.collect  state ->
              when(state)
                  is State.Loading -> 
                      showLoading()
                  
                  is State.Success -> 
                      Timber.d("I m in Success" + state.data)
                      val intent = Intent(this@LoginActivity,MainActivity::class.java)
                      startActivity(intent)
                      closeLoading()
                      finish()

                  
                  is State.Error -> 
                      val errorResponse = gson.fromJson(state.message,LoginResponse::class.java)
                      showToast(errorResponse.messages)
                      closeLoading()
                  
              
          
        
    
private fun loginListener() 
        mViewBinding.neumorphButtonSignIn.setOnClickListener 
            observeLogin()
            phoneNumber = mViewBinding.edtPhoneNumber.text.toString()
            pin = mViewBinding.oldPIN.text.toString()

            if (phoneNumber.isValidPhone()) 
                sendLoginCredentials(phoneNumber ,pin)
            
        else 


                mViewBinding.edtPhoneNumber.snack("Please Enter valid phone number") 
                    action("ok") 
                        dismiss()
                    
                


            
        
    
    private fun sendLoginCredentials(phoneNumber: String , pin: String) = mViewModel.postLoginCredentials("03XXXX" , "1234")

登录视图模型

@ExperimentalCoroutinesApi
@HiltViewModel
class LoginViewModel @Inject constructor(
    private val loginRepository: LoginRepository,

) : ViewModel() 
    private val _loginCredentials: MutableStateFlow<State<LoginResponse>> = MutableStateFlow(State.Empty())
    val loginCredentials: StateFlow<State<LoginResponse>> get() = _loginCredentials

    fun postLoginCredentials(phoneNumber: String, pin: String) 
        Timber.d("postLoginCredentials: $phoneNumber + $pin")
        _loginCredentials.value = State.loading()
        viewModelScope.launch 
            loginRepository.login(LoginRequest(phoneNumber,pin))
                .map  response -> State.fromResource(response) 
                .collectstate -> _loginCredentials.value = state 
        
    


登录库

class LoginRepository @Inject constructor(
    private val apiInterface: APIInterface
) 

    fun login(loginRequest: LoginRequest): Flow<ResponseAPI<LoginResponse>> 
        return object : NetworkBoundRepository<LoginRequest, LoginResponse>() 

            override suspend fun fetchFromRemote(): Response<LoginResponse> = apiInterface.createLoginRequest(
               loginRequest
           )
        .asFlow()
    

NetworkBoundRepository

abstract class NetworkBoundRepository<RESULT, REQUEST> 

    fun asFlow() = flow<ResponseAPI<REQUEST>> 

        val apiResponse = fetchFromRemote()

        val remotePosts = apiResponse.body()

        if (apiResponse.isSuccessful && remotePosts != null) 
            emit(ResponseAPI.Success(remotePosts))
         else 
            // Something went wrong! Emit Error state.
            emit(ResponseAPI.Failed(apiResponse.errorBody()!!.string()))
        

        
    .catch  e ->
        e.printStackTrace()
        emit(ResponseAPI.Failed("Network error! Can't get latest posts."))
    


    @MainThread
    protected abstract suspend fun fetchFromRemote(): Response<REQUEST>

有什么方法可以创建 MainAcitivity 的一个实例而忽略较旧的发出响应?任何可以工作的操作员。非常感谢这方面的任何帮助。谢谢。

【问题讨论】:

【参考方案1】:

实际上,当我将其移至onCreate() 时,我从登录单击侦听器中调用了observeLogin(),这在我的项目中造成了这种混乱。一切都按预期进行。所以,把这个发给不会坚持这个的新手。

【讨论】:

以上是关于StateFlow:收集后取消旧的发射状态的主要内容,如果未能解决你的问题,请参考以下文章

ViewModel中的StateFlow和SharedFlow,使用建议以及单元测试

ViewModel中的StateFlow和SharedFlow,使用建议以及单元测试

ui中再次收集StateFlow最后一个值

收集 StateFlow 时 Api 没有响应不会更改 kotlin

从多个状态流中收集

Simulink Stateflow:向状态添加不变量