Sinyuk
Sinyuk

Reputation: 287

Update request header when access token updates Dagger and Retrofit

I want to update access token in network request.But there is some difficulty using Dagger and Retrofit.

😢Sorry,my English is not good , so give you an example may be much clear.Starting from scratch, my idea is like this:

provide an access token saved in shared preference

@Provides
@ForOauth
Preference<String> provideAccessToken(RxSharedPreferences prefs) {
    return prefs.getString(PrefsUtils.KEY_ACCESS_TOKEN);
}

use access token to create an interceptor and added into okhttp client

@Provides
@Singleton
@Named("Cached")
public OkHttpClient provideOkHttpClientWithCache(Application application, @ForOauth OauthInterceptor oauthInterceptor) {
      ...
    builder.addInterceptor(oauthInterceptor);
      ...
}

and I provide the OauthInterceptor instance by its constructor

@Inject
public OauthInterceptor(@ForOauth Preference<String> accessToken) {
    this.accessToken = accessToken;
    Timber.tag("OauthInterceptor");
}

But cause the okhttp client is a singleton,it won't change when the access token in prefs updates.An alternative way I thought that may work is to use a custom scope like @ForOauth or something, but it's just a rough sketch...

By the way, I have another idea like this:

get the access token from prefs in the intercept() method , so every time I can have a request header which contains the latest access token.

@Override
public Response intercept(Chain chain) throws IOException {
    Request.Builder builder = chain.request().newBuilder();
    if (accessToken.isSet()) {
        // Preference<String> accessToken
        builder.header("Authorization", ACCESS_TYPE + accessToken.get());
    } else {
        builder.header("Authorization", "Bearer xxxxxx");
    }
    return chain.proceed(builder.build());
}

But I haven't really experimented with this idea,and I think it's not right 😂

I wonder whether I have to create a new okhttp client instance every time or I can just update the access token then the okhttp client singleton can refresh its interceptor...

So could you please give me some advice , or a simple working example.

Thanks in advance 😊

Upvotes: 4

Views: 2945

Answers (1)

Aystub
Aystub

Reputation: 1642

Hmmmm, I've done this many times and never noticed any issues with the access token refresh not making its way down the chain to OkHttp. Here's a typical setup I use in apps:

@Provides @Singleton
SharedPreferences providePreferences(Context ctx) {
    return new SharedPreferences(ctx);
}

@Provides @Singleton
HttpLoggingInterceptor provideLoggingInterceptor(){
    return new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY);
}

@Provides @Singleton
OkHttpClient provideClient(HttpLoggingInterceptor interceptor, SharedPreferences prefs){
    return new OkHttpClient.Builder()
            .addNetworkInterceptor(chain -> {
                // Add Auth Header
                String token = prefs.accessToken().get();
                if(token == null) token = "";

                Request request = chain.request().newBuilder().addHeader("Authorization", token).build();
                return chain.proceed(request);
            })
            .addInterceptor(interceptor)
            .build();
}

@Provides @Singleton
Retrofit provideRetrofit(@ApiUrl String url, OkHttpClient client){
    return new Retrofit.Builder()
            .baseUrl(url)
            .client(client)
            .addConverterFactory(LoganSquareConverterFactory.create())
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .build();
}

SharedPreferences is just a class I've abstracted some of the RxSharedPreferences logic into. Can also just @Inject it wherever you need it in the app that way too, which is nice. Here's a simple version of that class just for fun:

public class SharedPreferences {
    // Constants and variables
    private static final String PREFERENCE_FILENAME = BuildConfig.APPLICATION_ID + ".prefs";
    private static final String PREF_ACCESS_TOKEN= "pref_access_token";

    private RxSharedPreferences mRxSharedPrefs;

    // Constructor
    public SharedPreferences(Context context) {
        mRxSharedPrefs = RxSharedPreferences.create(context.getSharedPreferences(PREFERENCE_FILENAME, Context.MODE_PRIVATE));
    }

    // Helper methods
    public Preference<String> accessToken() { return mRxSharedPrefs.getString(PREF_ACCESS_TOKEN, ""); }

    public void logout() { accessToken().delete(); }
}

Upvotes: 2

Related Questions