Skip to content

Instantly share code, notes, and snippets.

@addam01
Last active November 1, 2018 07:40
Show Gist options
  • Save addam01/ebe7b797e4bddc1804061806709b24d2 to your computer and use it in GitHub Desktop.
Save addam01/ebe7b797e4bddc1804061806709b24d2 to your computer and use it in GitHub Desktop.
Android RxKotlin and OKHttp Interceptor

Sample for using OkHTTP interceptor for RxKotlin and Retrofit

  • Interceptor will add the token into preference once after login
  • Interceptor try to run the request once using current token in the OKHttp builder class
  • If failed, it'll intercept by calling TokenRefreshInterceptor.kt
  • TokenRefreshInterceptor.kt will try again, if got 200 from API
    • It'll call the refresh API by appending refresh token from preference to refresh the token
    • Then proceed to call original request in makeTokenRefreshCall
public class SampleOkHttpClient{
fun provideOkHttpClient(application: Application, tokenRepository: TokenRepository,
schedulerProvider: SchedulerProvider, iPayEasyPreference: IPayEasyPreference): OkHttpClient {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val tokenInterceptor = TokenRefreshInterceptor(tokenRepository, schedulerProvider, iPayEasyPreference)
val cacheDir = File(application.cacheDir, UUID.randomUUID().toString())
val cache = Cache(cacheDir, 10 * 1024 * 1024)
return OkHttpClient.Builder()
.cache(cache)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.addInterceptor { chain ->
Timber.d{"Added Authorization"}
val original = chain.request()
val builder = original.newBuilder()
builder.addHeader("Authorization", "Bearer " + iPayEasyPreference.getAccessToken())
val request = builder.build()
chain.proceed(request)
}
.addInterceptor(interceptor)
.addInterceptor(tokenInterceptor)
.build()
}
}
class SampleRetrofit{
fun provideGeneralService(gson: Gson, okHttpClient: SampleOkHttpClient): GeneralService {
val baseUrl = if (Constants.isProduction()) UAT_URL else DEV_URL
return Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(okHttpClient)
.build().create(GeneralService::class.java)
}
class SchedulerProvider(private val backgroundScheduler: Scheduler, private val foregroundScheduler: Scheduler) {
fun <T> getSchedulersForSingle(): (Single<T>) -> Single<T> {
return { single: Single<T> ->
single.subscribeOn(backgroundScheduler)
.observeOn(foregroundScheduler)
}
}
class TokenRefreshInterceptor() : Interceptor {
private lateinit var token: String
lateinit var appPreference : AppPreference
lateinit var tokenRepository: TokenRepository
lateinit var schedulerProvider: SchedulerProvider
constructor(tokenRepository: TokenRepository, schedulerProvider: SchedulerProvider, appPreference : AppPreference): this(){
this.appPreference = appPreference
this.tokenRepository = tokenRepository
this.schedulerProvider = schedulerProvider
token = this.appPreference.getAccessToken()
}
override fun intercept(chain: Interceptor.Chain?): Response {
val request = chain!!.request()
// If request got response 403, then create new request to refresh token
val response = chain.proceed(request)
if(response.code() == 200){
return response
}
else if(response.code() == 403 || response.code() == 401){
Timber.e { "Interceptor: " + response.code() }
Timber.e{"Interceptor: " + request.url().toString()}
lateinit var r: Response
try{
r = makeTokenRefreshCall(request, chain)
}catch (e: JSONException){
e.printStackTrace()
}
Timber.e{"Intercepted: " + r.toString()}
return r
}
Timber.e{"Intercepted: " + response.toString()}
return response
}
// Handle refresh expired token
private fun makeTokenRefreshCall(request: Request?, chain: Interceptor.Chain): Response {
Timber.e{"Retrying new request"}
/* retch refresh token, some synchronus API call */
fetchToken()
Timber.d{ "New token generated -> $token" }
/* make a new request which is same as the original one, except that its headers now contain a refreshed token */
val newRequest = request!!.newBuilder().header("Authorization", "Bearer $token").build()
val another = chain.proceed(newRequest)
if(token != null && !token.isNullOrEmpty()){
if (another.code() == 403){
makeTokenRefreshCall(newRequest, chain)
}else return another
}
return another
}
private fun fetchToken() {
Timber.e { "Refresh token ${iPayEasyPreference.getRefreshToken()}" }
callRefreshToken(iPayEasyPreference.getRefreshToken()).subscribeBy(
onSuccess = {
iPayEasyPreference.setAccessToken(it.access_token!!)
iPayEasyPreference.setRefreshToken(it.refresh_token!!)
iPayEasyPreference.setTokenType(it.token_type!!)
token = it.access_token
},onError = {
Timber.e{it.message!!}
token = ""
})
}
//Use the repo for API calling in RxKotlin
private fun callRefreshToken(refreshToken: String): Single<UserLoginResponse> =
tokenRepository.reGenerateToken()
.compose(schedulerProvider.getSchedulersForSingle())
}
//Add the repo API in the instance api: TokenGenerationService
class TokenRepository constructor(private val api: TokenGenerationService){
fun reGenerateToken(authorization: String, contentType: String, refreshToken: String, grantType: String): Single<UserLoginResponse> =
api.reGenerateToken(authorization, contentType, grantType, refreshToken)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment