Повторно используемый механизм для прослушивания LiveData на предмет конкретных ошибок в Android

#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