ViewModel,存储库和改造-在哪里以及如何进行?

时间:2018-11-25 15:01:46

标签: android kotlin coroutine android-architecture-components kotlinx.coroutines

我已经尝试了2个多星期的Android体系结构模式。我一直在努力了解将代码放在ViewModel或Repository或其他地方的什么地方?

当我学习和浏览示例应用程序时,它看起来真的很棒。因此,我决定在现实生活中使用它。我想在单击登录按钮时执行登录时卡住了。我不明白流程应该如何进行。

第一件事是要拯救这个github问题线程https://github.com/googlesamples/android-architecture-components/issues/63#issuecomment-310422475

在用户单击按钮时触发事件。所有示例和博客文章都非常适合指向片段开始时加载数据并观察事物。但是,这东西对我没有用。因此,我提出了自己的解决方案,观察util是否达到了最终状态。就像网络资源开始加载一样,并以成功或错误完成。这意味着在数据加载开始和完成时将有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调用返回数据。如何从存储库端抽象事物并使用协程简化事物。我来到this article,并且在阅读后做了一些事情。

我想到了这样的东西:

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一样,它们都可以告诉我结果的状态(成功或失败以及STATE_SUCCESS或STATE_FAILURE)。因此,我没有使事情变得更加清晰,而是使它们变得不清楚。有什么更好的办法吗? (这肯定不是更好的选择)

我发现很难理解的另一件事是,当登录响应出现时,我应该在哪里编写存储用户信息的逻辑?就像在存储库中一样,我应该编写将其存储到首选项中的逻辑吗?还是在视图模型内部?有什么更好的方法?

我找不到实际只进行改造的联网示例。我应该如何处理和设计这类应用?

0 个答案:

没有答案