Как использовать liveata в архитектуре MVVM - PullRequest
0 голосов
/ 18 января 2020

TLDR: Как я могу правильно реализовать архитектуру MVVM с LiveData?

У меня есть класс фрагмента, который наблюдает за живыми данными, выставленными viewModel:

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

Представление класса модели просто запрашивает сопоставленный объект liveata.

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

Как в этом случае правильно реализовать архитектуру MVVM с LiveData?

Я также мог бы переместить логи c Теперь у меня есть LoginUseCase в ModelView и затем что-то подобное, что позволяет избежать проблемы, описанной ранее. Но тогда я не могу делегировать отображение / обработку ошибок для использования case.

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

1 Ответ

1 голос
/ 18 января 2020

Вы пытаетесь наблюдать изменчивые LiveData, которые инициализируются только после onClickListener, поэтому вы не сможете заставить их работать, также у вас есть свойство 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
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...