ViewModel, Repository и Retrofit - что и где происходит? - PullRequest
0 голосов
/ 25 ноября 2018

Я экспериментирую с архитектурой Android более 2 недель.Я изо всех сил пытался понять, что код куда, ViewModel или Repository или где-то еще?

Это действительно выглядело хорошо, когда я изучал и просматривал примеры приложений.Итак, я решил использовать его в реальной жизни.Я застрял, когда я хотел выполнить вход в систему при нажатии кнопки входа в систему.Я не мог понять поток, как он на самом деле должен идти.

Первое, что нужно было спасти, была эта тема выпуска github, https://github.com/googlesamples/android-architecture-components/issues/63#issuecomment-310422475

Запуск события, когда пользователь нажимает кнопку.Все образцы и посты в блогах были действительно хороши, указывая на загрузку данных, когда фрагмент начинается, и наблюдая за чем-либоНо эта вещь не сработала для меня.Поэтому я предложил собственное решение, которое наблюдает за тем, как оно достигает конечного состояния.Как сетевой ресурс начинает загружаться, и завершается с успехом или ошибкой.Это означает, что у наблюдателя будет 2 обратных вызова, когда начнется загрузка данных и когда она завершится.

Это выглядело примерно так:

interface StatefulResource<T> {

    /**
     * This method return if the resource is in it's end state. All the events had occurred
     * and there are no other events left to follow for them.
     *
     * @return true/false based if the current state is end state or not
     */
    fun isEndState(): Boolean

}

class Resource<T> private constructor() : StatefulResource<T> {

    var state: Int? = null
        private set
    var result: Result? = null

    companion object {
        const val STATE_LOADING = 1
        const val STATE_SUCCESS = 2
        const val STATE_ERROR = 3

        fun <T> result(result: Result) = Resource<T>().apply { this.result = result }
        fun <T> loading() = Resource<T>().apply { this.state = STATE_LOADING }
    }

    override fun isEndState() = (state == STATE_ERROR) or (state == STATE_SUCCESS)

    fun isSuccessful() = state == STATE_SUCCESS
}

fun <J : StatefulResource<T>, T> LiveData<J>.observeStatefully(
    lifecycleOwner: LifecycleOwner,
    observer: Observer<J>
) {
    val liveData = this
    val internalObserver = object : Observer<J> {
        override fun onChanged(resource: J) {
            observer.onChanged(resource)
            if (resource.isEndState().orFalse()) {
                liveData.removeObserver(this)
            }
        }
    }
    observe(lifecycleOwner, internalObserver)
}

Теперь, что на самом деле здесь произошло, так это то, каквернуть данные из вызова API.Как абстрагировать вещи со стороны хранилища и упростить вещи, используя сопрограммы.Я подошел к этой статье и сделал что-то после прочтения.

Я придумал что-то вроде этого:

sealed class Result
data class Success<out T : Any>(val data: T?) : Result()
data class Failure(val message: String, val error: Throwable?) : Result()

interface CommonResponse<T : Any> {
    var status: Int
    var message: String
    var data: T?

    fun isValid() = status == 1

    fun mapResult(): Result = if (isValid())
        Success(data)
    else
        Failure(message, null)
}

/**
 * This extension method enqueues the call using the coroutine and
 * return the Result instance with Success or Failure
 */
suspend fun <T, S : CommonResponse<T>> Call<S>.getResult(): Result = try {
    this.enqueueAwait().mapResult()
} catch (error: Throwable) {
    Failure("Something went wrong", error)
}

Теперь в своей деятельностинаписать этот код:

  viewModel.login(mBinding.editEmail.value.trim(), mBinding.editPassword.value, null)
                    .observeStatefully(viewLifecycleOwner, Observer { resource ->
                        mBinding.resource = resource

                        when (resource.state) {
                            Resource.STATE_SUCCESS -> {
                                navController.navigate(R.id.nav_action_fragment_sign_in_to_fragment_home)
                            }
                        }
                    })

Я думаю, я продублировал некоторые вещи с этим шаблоном.Как и закрытый класс Result и Resource, оба могут сообщать мне состояние результата (Success или Failure и STATE_SUCCESS или STATE_FAILURE).Таким образом, вместо того, чтобы сделать вещи более ясными, я сделал их неясными.Есть ли лучший способ?(Это, конечно, не лучший вариант)

Еще одна вещь, которую я нахожу трудной для понимания, - когда приходит ответ при входе в систему, куда мне писать логику хранения пользовательской информации?Как в репозитории я должен написать свою логику сохранения в префе?Или внутри модели вида?Какой способ лучше?

Я не смог найти пример, который на самом деле выполняет только сетевую модернизацию.Как мне обрабатывать и разрабатывать такие приложения?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...