开发者社区> 问答> 正文

如何在MVVM架构中使用实时数据

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()))
            }
        }
}

展开
收起
Puppet 2020-01-18 10:31:32 487 0
1 条回答
写回答
取消 提交回答
  • 您正在尝试观察一个可变的LiveData,该LiveData仅在之后初始化,onClickListener因此您将无法使用它,并且您还拥有一个lateinit仅在您调用将引发异常的login方法时才初始化的属性。

    为了解决您的问题,您可以使用a 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
            }
        }
    }
    
    2020-01-18 10:31:47
    赞同 展开评论 打赏
问答排行榜
最热
最新

相关电子书

更多
MaxCompute Serverless 架构演进 立即下载
阿里云消息队列的 Serverless架构演进 立即下载
青团社云原生架构实践—亿级灵活用工平台的架构实践 立即下载