Я экспериментирую с архитектурой 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).Таким образом, вместо того, чтобы сделать вещи более ясными, я сделал их неясными.Есть ли лучший способ?(Это, конечно, не лучший вариант)
Еще одна вещь, которую я нахожу трудной для понимания, - когда приходит ответ при входе в систему, куда мне писать логику хранения пользовательской информации?Как в репозитории я должен написать свою логику сохранения в префе?Или внутри модели вида?Какой способ лучше?
Я не смог найти пример, который на самом деле выполняет только сетевую модернизацию.Как мне обрабатывать и разрабатывать такие приложения?