如何在MVVM体系结构中使用实时数据

问题描述 投票:0回答:1

TLDR:如何使用LiveData正确实现MVVM体系结构?

我有一个片段类,它观察暴露了viewModel的livedata:

viewModel.loginResultLiveData.observe

class LoginFragment : Fragment() {

    private lateinit var binding: FragmentLoginBinding

    private val viewModel by fragmentScopedViewModel { injector.loginViewModel }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = FragmentLoginBinding.inflate(inflater, container, false)

        val username = binding.loginInputField.toString()
        val password = binding.passwordInputField.toString()

        binding.loginSignInButton.setOnClickListener {  viewModel.login(
            username,
            password
        ) }

        viewModel.loginResultLiveData.observe(viewLifecycleOwner){
            when(it){
                is LoginResult.Success -> doSmth()
            }
        }

        return binding.root
    }
}

视图模型类仅要求映射的livedata对象。

class LoginViewModel @Inject internal constructor(
    private val loginUseCase: LoginUseCase
) : ViewModel() {
    lateinit var loginResultLiveData: MutableLiveData<LoginResult>

    fun login(username: String, password: String) {
        loginResultLiveData = loginUseCase.login(username, password)
    }
}

模型使用用例,以原始格式映射结果,也将映射错误:

class LoginUseCase @Inject internal constructor(
    private val authRepository: AuthEmailRepository
) {
    var loginResultLiveData = MutableLiveData<LoginResult>()

    fun login(userName: String, password: String): MutableLiveData<LoginResult> {
        authRepository.login(userName, password)
            .addOnCompleteListener {
                if (it.isSuccessful) {
                    loginResultLiveData.postValue(LoginResult.Success)
                } else {
                    loginResultLiveData.postValue(LoginResult.Fail(it.exception.toString()))
                }
            }
        return loginResultLiveData
    }
}

问题在于,仅在单击loginSignInButton之后,模型才会创建liveData对象。但是在设置onClickListener之后,我立即开始观察该对象。同样,每次单击按钮时,都会创建一个新的viewModel.loginResultLiveData实例,这没有任何意义。

  binding.loginSignInButton.setOnClickListener {  viewModel.login(
        username,
        password
    ) }

    viewModel.loginResultLiveData.observe(viewLifecycleOwner){
        when(it){
            is LoginResult.Success -> doSmth()
        }
    }

在这种情况下,如何使用LiveData正确实现MVVM体系结构?

我也可以将现在在LoginUseCase中拥有的逻辑移到ModelView,然后再有类似的东西,这避免了前面描述的问题。但是,然后我无法将映射/错误处理委托给用例。

class LoginViewModel @Inject internal constructor(
    private val loginUseCase: LoginUseCase
) : ViewModel() {
    val loginResult: MutableLiveData<LoginResult> = MutableLiveData()

    fun login(username: String, password: String) = loginUseCase.login(username, password)
        .addOnCompleteListener {
            if (it.isSuccessful) {
                loginResult.postValue(LoginResult.Success)
            } else {
                loginResult.postValue(LoginResult.Fail(it.exception.toString()))
            }
        }
}
android callback firebase-authentication android-livedata android-architecture
1个回答
0
投票

您正在尝试观察一个仅在onClickListener之后初始化的可变LiveData,因此您将无法使用它,而且还有一个lateinit属性,仅当您调用登录方法时才会初始化,该属性将抛出例外。

为了解决您的问题,您可以使用MediatorLiveData来观察您的其他实时数据,并将结果传递回片段观察器。

您可以尝试以下操作:

class LoginViewModel @Inject internal constructor(
    private val loginUseCase: LoginUseCase
) : ViewModel() {

    private var _loginResultLiveData = MediatorLiveData<LoginResult>()
    val loginResultLiveData: LiveData<LoginResult> = _loginResultLiveData

    fun login(username: String, password: String) {
        val loginUseCaseLiveData = loginUseCase.login(username, password)
        _loginResultLiveData.addSource(loginUseCaseLiveData) {
            _loginResultLiveData.value = it
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.