实时数据在更改片段时再次赋予旧价值

Pri*_*ain 6 android kotlin android-livedata android-viewmodel android-jetpack

我正在跨多个片段使用来自共享 ViewModel 的实时数据。我有一个登录片段,它需要用户的电话号码和密码,然后用户按下登录按钮,我为此调用 API,现在如果登录失败,我将显示一个 toast“登录失败”,现在如果用户转到“ForgotPassword”屏幕,该屏幕也使用与“SignInFragment”相同的视图模型,然后从忘记密码屏幕按回,出现登录片段,但它再次显示 toast“登录失败”,但 API没有被调用,它从之前注册的观察者那里获取数据,那么有什么方法可以解决这个问题吗?

SignInFragment.kt

class SignInFragment : Fragment() {

    private lateinit var binding: FragmentSignInBinding

    //Shared view model across two fragments
    private val onBoardViewModel by activityViewModels<OnBoardViewModel>()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = DataBindingUtil.inflate(
            inflater,
            R.layout.fragment_sign_in,
            container,
            false
        )
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        onBoardViewModel.signInResponse.observe(viewLifecycleOwner) { response ->
            //This is calling again after coming back from new fragment it.
            showToast("Sign In Failed")
        }
    }

    override fun onClick(v: View?) {
        when (v?.id!!) {
            R.id.forgotPasswordTV -> {
                findNavController().navigate(SignInFragmentDirections.actionSignInFragmentToForgotPasswordFragment())
            }
            R.id.signInTV -> {
                val phoneNumber = binding.phoneNumberET.text
                val password = binding.passwordET.text
                val signInRequestModel = SignInRequestModel(
                    phoneNumber.toString(),
                    password.toString(),
                    ""
                )

                //Calling API for the sign-in
                onBoardViewModel.callSignInAPI(signInRequestModel)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

忘记密码片段

class ForgotPasswordFragment : Fragment() {

    private lateinit var binding: FragmentForgotPasswordBinding

    //Shared view model across two fragments
    private val onBoardViewModel by activityViewModels<OnBoardViewModel>()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = DataBindingUtil.inflate(
            inflater,
            R.layout.fragment_forgot_password,
            container,
            false
        )
        return binding.root
    }
}
Run Code Online (Sandbox Code Playgroud)

板载视图模型

class OnBoardViewModel : ViewModel() {

    private var repository: OnBoardRepository = OnBoardRepository.getInstance()

    private val signInRequestLiveData = MutableLiveData<SignInRequestModel>()

    //Observing this data in sign in fragment
    val signInResponse: LiveData<APIResource<SignInResponse>> =
        signInRequestLiveData.switchMap {
            repository.callSignInAPI(it)
        }

    //Calling this function from sign in fragment
    fun callSignInAPI(signInRequestModel: SignInRequestModel) {
        signInRequestLiveData.value = signInRequestModel
    }

    override fun onCleared() {
        super.onCleared()
        repository.clearRepo()
    }
}
Run Code Online (Sandbox Code Playgroud)

我尝试将此代码移至 onActivityCreated 内,但从新片段返回后仍然会被调用。

 onBoardViewModel.signInResponse.observe(viewLifecycleOwner) { response ->
            showToast("Sign In Failed")
        }
Run Code Online (Sandbox Code Playgroud)

Ser*_*gey 7

使用SingleLiveEventclass 而不是LiveDatain OnBoardViewModelclass 将解决您的问题:

val signInResponse: SingleLiveEvent <APIResource<SignInResponse>>

class SingleLiveEvent<T> : MutableLiveData<T>() {

    private val pending = AtomicBoolean(false)

    override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
        super.observe(owner, Observer<T> { t ->
            if (pending.compareAndSet(true, false)) {
                observer.onChanged(t)
            }
        })
    }

    override fun setValue(t: T?) {
        pending.set(true)
        super.setValue(t)
    }

    fun call() {
        postValue(null)
    }

}
Run Code Online (Sandbox Code Playgroud)

这是一个生命周期感知的可观察对象,在订阅后仅发送新的更新。仅LiveData当显式调用 setValue() 或 call() 时,才会调用 observable。