#android #android-livedata
#Android #android-livedata
Вопрос:
У меня есть интеграция с серверной частью, выполненная с помощью модернизации. Архитектура моего проекта похожа на известный пример GithubBrowserSample, предложенный Google в качестве примера. Это означает, что в моей ViewModel есть некоторые живые данные, которые можно наблюдать
Любой запрос может выдать «401 несанкционированный», и я хотел бы перенаправить на фрагмент входа. И я могу сделать это при проверке сведений об ошибках во фрагменте во время наблюдения
viewModel.user.observe(viewLifecycleOwner, { response ->
if (response.status == Status.SUCCESS) {
// do work there
} else if (response.status == Status.ERROR) {
// check does this 401 and redirect
}
})
Но у меня в моем приложении много таких мест, и мне нужен какой-то механизм
для прослушивания 401 и перенаправления, который можно применять ко всем местам из коробки, не проверяя в каждом месте
Скажем, некоторая оболочка, некоторые утилиты для повторного использования проверяют наличие конкретной ошибки
Ответ №1:
Создайте базовый класс, создайте класс, который расширяет возможности appcompactivity, означает, что базовый класс будет родительским действием и будет прослушивать пользователя. наблюдайте там и расширяйте свою текущую активность родительской активностью :
class parentActivity extends AppCompactActivity() {
onCreate(){
////Listener for the observer here
this.user.obser{
}
}
}
//////////////////////////
class yourActivity extends parentActivity(){
//TODO your code
}
Комментарии:
1. P.S: Я только что поделился концепцией, примером которой является код, которым я поделился.
2. Вы имеете в виду, чтобы сохранить связанный с пользователем токен на уровне сохранения, а затем наблюдать за существованием этого токена при некоторой базовой активности?
3. Да, это может быть решением вашей проблемы.
Ответ №2:
Это может не ограничивать проверку, которую вам нужно выполнить, но она перемещает логику из Activity в ViewModel (где ее легче протестировать).
В вашей ViewModel
val user: LiveData<Result<User>> = repository.getUser() // put your actual source of fetching the user here
val needsRedirectToLogin: LiveData<Boolean> = Transformations.map(user) { response ->
response.code == 401
}
Улучшение: Для этого можно создать функцию расширения, которую можно повторно использовать в других ViewModels для того же сценария:
/**
* Returns true when [code] matches the Response code.
*/
fun LiveData<Response<Any>>.isErrorCode(code: Int): LiveData<Boolean> = Transformations.map(this) { response ->
response.code == code
}
А затем используйте его как:
val needsRedirectToLogin: LiveData<Boolean> = user.isErrorCode(401)
В вашем фрагменте:
viewModel.needsRedirectToLogin.observe(viewLifecycleOwner, { redirectToLoginPage ->
if (redirectToLoginPage) {
// do the redirect here.
}
Следующим шагом может быть использование EventBus или BroadcastReceiver для отправки этого события. Так что у вас есть только одно место для обработки этого.
Есть много способов, как идти отсюда (в зависимости от ваших требований). Но я надеюсь, что это даст вам представление и поможет вам.
Комментарии:
1. Я думаю, это интересный подход, но мне нужно настроить его так, чтобы он был легко применим к большому количеству таких живых данных
Ответ №3:
Если ошибка, которую вы хотите обработать, специфична для 401 Unauthorized
, вы можете реализовать Authenticator
и прикрепить ее к своему okhttp
.
Сначала создайте свой Authenticator
класс
class YourAuthenticator : Authenticator {
// This callback is only called when you got 401 Unauthorized response
override fun authenticate(route: Route?, response: Response): Request? {
// Navigate to Login fragment
}
}
Затем добавьте Authenticator
в свой конструктор okhttp
val yourAuthenticator = YourAuthenticator()
val okHttp = OkHttpClient.Builder()
.authenticator(yourAuthenticator)
.build()
Комментарии:
1. Я знаю об аутентификаторе, но я не уверен, что смогу перейти от него к нужному фрагменту. Для навигации требуется контекст, но я не уверен, что у меня будет необходимый контекст в Authenticator