Перехватчик Retrofit2, использующий токен только в определенных методах

#android #authorization #retrofit2 #interceptor #okhttp3

#Android #авторизация #retrofit2 #перехватчик #okhttp

Вопрос:

Я хочу использовать authorization в Retrofit2 . Я выбрал interceptor способ, потому что почти все методы требуют authorization , за исключением одного, где я получаю токен для дальнейшего использования с другими методами.

Мой вопрос: как я могу этого добиться?

Я храню свой токен доступа в SharedPreferences . Моей первой идеей было: «Я могу извлечь его в своем RetrofitClient классе и в зависимости от того, равен ли токен нулю или у него есть значение, создать подходящий Retrofit экземпляр» (с добавлением к нему .client() или без него.).

К сожалению, это не так просто, потому что Context необходимо получить настройки.

Я не знаю, как передать его в RetrofitClient класс или как добраться до него внутри.

Вот мой RetrofitClient класс:

 public class RetrofitClient {

    public static final String BASE_URL = "http://localhost";
    public static Retrofit retrofit = null;

    private RetrofitClient() {
        retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                //.client(getRequestHeader())
                .build();
    }

    /*OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request newRequest = chain.request().newBuilder()
                    .addHeader("Authorization", "Bearer"   )
        }
    })*/

    public static synchronized Retrofit getInstance() {
        if (retrofit == null) {

            retrofit = new Retrofit.Builder().baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create()).build();
        }
        return retrofit;
    }

    public Api getApi() {
        return retrofit.create(Api.class);
    }
}
  

Мой getRequestHeader() предыдущий метод, в котором я устанавливал тайм-аут.

Я хочу создать экземпляр, подобный этому:

 private RetrofitClient() {
            tokenService.token = null;
            tokenService.readToken(ApplicationContext.getInstance());


            if (tokenService.token == null) {
                retrofit = new Retrofit.Builder()
                        .baseUrl(BASE_URL)
                        .addConverterFactory(GsonConverterFactory.create())
                        .build();
            } else {
                retrofit = new Retrofit.Builder()
                        .baseUrl(BASE_URL)
                        .addConverterFactory(GsonConverterFactory.create())
                        .client(client) //I have interceptor here with access token
                        .build();
            }
        }
  

И затем:
RetrofitClient.getInstance().create(Api.class).getToken() //получение токена
RetrofitClient.getInstance().create(Api.class).someMethod() // когда я получаю токен

Хороший ли это подход?

Ответ №1:

Вы можете использовать контекст приложения, который доступен, пока ваше приложение работает. Получите его из своего класса app:

 public class MyApp extends Application {
    private static MyApp instance;
    @Override
    public void onCreate() {
    super.onCreate();
    instance = this;
    //other code
}
public static MyApp getInstance() {
    return instance;
}
  

А затем просто используйте MyApp.getInstance() , чтобы получить контекст там, где вам нужно. С помощью этого подхода вы можете достичь того, о чем думали раньше, и получить SharedPreferences в RetrofitClient классе.

Не забудьте добавить свой класс приложения в manifest.

ОБНОВЛЕНИЕ: Вы создаете свой экземпляр retrofit только один раз, поэтому, если вы создадите его, когда у вас будет токен — вы всегда будете использовать его, пока приложение работает. Я думаю, хорошей практикой является проверка наличия токена в вашем перехватчике. Что-то вроде этого:

 new Interceptor() {
        @Override
        public Response intercept(@NonNull Chain chain) throws IOException {
            Request request = chain.request();
            if (tokenService.token == null) {
                return chain.proceed(request);
            } else {
                return chain.proceed(request.newBuilder().addHeader("Authorization", "Bearer"   tokenService.token).build());
            }
        }
    }
  

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

Комментарии:

1. И последний простой вопрос. Если я вызову, RetrofitClient.getInstance().create(Api.class).getToken() а затем RetrofitClient.getInstance().create(Api.class).someMethod() для чего требуется токен, будет ли он вести себя хорошо? Пожалуйста, проверьте модификацию конструктора в моей правке.

2. @SkypeDogg Я обновил свой ответ, надеюсь, теперь это понятно

3. Класс TokenService имеет одно строковое поле для токена и три метода: saveToken() , readToken() и clearToken() но каждый раз, когда я включаю приложение, я запрашиваю токен с PIN-кодом, но я использую следующие методы, только если токен возвращается с кодом 200 , в противном случае он сообщает о неправильном PIN-коде. Каждый раз, когда возвращается новый токен, он переопределяет старый PIN, поэтому, я надеюсь, ошибок быть не должно, даже без clearToken() метода

4. @SkypeDogg отлично, надеюсь, я тебе немного помог

Ответ №2:

Просто добавьте SharedPreferences в качестве параметра конструктора в RetrofitClient или другом, в зависимости от ситуации

Ответ №3:

Вы можете сделать следующим образом:

Сначала создайте класс предпочтений с помощью экземпляра singleton

Настройки

 public class Preferences {

    public static Preferences INSTANCE;
    private final String PREFERENCE_FILE = "my_preferences";
    public final String PREF_TOKEN = "pref_user_token";
    private SharedPreferences mPrefs;
    private SharedPreferences.Editor mEdit;

    private Preferences() {
        Application app = MyApplicationClass.getInstance();
        mPrefs = app.getSharedPreferences(PREFERENCE_FILE, Context.MODE_PRIVATE);
        mEdit = mPrefs.edit();
    }

    // Returns singleton instance
    public static Preferences getInstance() {
        if (INSTANCE == null)
            INSTANCE = new Preferences();
        return INSTANCE;
    }

    public String getToken() {
        return Preferences.getInstance().mPrefs.getString(PREF_TOKEN, "");
    }

    public void saveToken(String value) {
        mEdit.putString(PREF_TOKEN, value);
        mEdit.apply();
    }

}
  

Затем измените свой класс RetrofitClient, как показано ниже:

RetrofitClient

 public class RetrofitClient {

    public static final String BASE_URL = "http://localhost";
    public static Retrofit retrofit = null;

    private RetrofitClient() {
        retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .client(mClient)
                .build();
    }

    OkHttpClient mClient = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request newRequest = chain.request().newBuilder()
                    .addHeader("Authorization", "Bearer"   Preferences.getInstance().getToken())
        }
    })

    public static synchronized Retrofit getInstance() {
        if (retrofit == null) {

            retrofit = new Retrofit.Builder().baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create()).build();
        }
        return retrofit;
    }

    public Api getApi() {
        return retrofit.create(Api.class);
    }
}