Reputation: 287
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
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