I have problem using dagger 2 for updating token in runtime.
So here is the scenario:
I have a screen to Change Password. when i succeed update password, the current jwt token would be invalid, and i need to store new token from update token response, i store that token in SharedPreferences. but the problem is when i store the token. it updated in sharedprefernces, but wont update value in DaggerGraph where i build Retrofit instance (Authorization header).
Below is my code :
AppComponent.kt
@Singleton
@Component(
modules = [StorageModule::class, AppModule::class, ViewModelModule::class]
)
interface AppComponent {
@Component.Factory
interface Factory {
fun create(@BindsInstance context: Context): AppComponent
}
fun inject(activity: SplashActivity)
fun inject(activity: LoginActivity)
fun inject(activity: MainActivity)
fun inject(activity: ChangePasswordActivity)
}
AppModule.kt
@Module
class AppModule {
@Singleton
@Provides
fun provideAuthInterceptor(sharedPreferencesSources: SharedPreferencesSources): Interceptor {
return AuthInterceptor(sharedPreferencesSources.tokenApi())
}
@Singleton
@Provides
fun provideApiService(
authInterceptor: Interceptor
): SharedProductClient {
return Network.retrofitClient(authInterceptor = authInterceptor)
.create(SharedProductClient::class.java)
}
@Singleton
@Provides
fun provideAppRepository(apiService: SharedProductClient): AppRepository {
return AppRepositoryImpl(apiService)
}
@Singleton
@Provides
fun provideAppUseCase(appRepository: AppRepository): AppUseCase {
return AppUseCase(appRepository)
}
@Singleton
@Provides
fun provideAppScheduler(): SchedulerProvider = AppSchedulerProvider()
}
StorageModule.kt
@Module
class StorageModule {
@Singleton
@Provides
fun provideSharedPreferences(context: Context): SharedPreferences {
return context.getSharedPreferences(SharedPrefName, Context.MODE_PRIVATE)
}
@Singleton
@Provides
fun provideSharedPreferencesSource(sharedPrefInstance: SharedPreferences): SharedPreferencesSources {
return SharedPreferencesSourcesImpl(sharedPrefInstance)
}
companion object {
const val SharedPrefName = "share_product_prefs"
}
}
AuthInterceptor.kt
class AuthInterceptor constructor(
private val token: String
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response = chain.run {
proceed(
request()
.newBuilder()
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Bearer $token")
.build()
)
}
}
Any suggestion would be really help me. Thanks!
This is because you only pass a String
instance of the token when creating the AuthInterceptor
.
You should provide a way (e.g. an interface) of dynamically obtaining the token from the SharedPreferences
when needed.
This is one way of doing this:
token:String
to a function type in your AuthInterceptor
constructor (and use it when needed):class AuthInterceptor constructor(
private val tokenProvider: () -> String
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response = chain.run {
proceed(
request()
.newBuilder()
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Bearer ${tokenProvider.invoke()}")
.build()
)
}
}
AuthInteceptor
build your lambda to dynamically refer to SharedPreferences
@Module
class AppModule {
@Singleton
@Provides
fun provideAuthInterceptor(sharedPreferencesSources: SharedPreferencesSources): Interceptor {
return AuthInterceptor(){ sharedPreferencesSources.tokenApi() }
}
//...
}
This way the tokenProvider
will be invoked
(SharedPreferences
will be accessed) every time you make an api call, instead of just a single time when creating the AuthInterceptor
.