Description
I am having trouble with Interceptor as it does not call onError
nor onSuccess
on specific scenario
This is my interface class
interface EndpointServices {
companion object {
private fun interceptor(): Interceptor {
return Interceptor { chain ->
val request: Request = chain.request()
val originalResponse: Response = chain.proceed(request)
originalResponse.newBuilder()
.build()
}
}
private fun onlineOfflineHandling(): Interceptor {
return Interceptor { chain ->
try {
Log.wtf("INTERCEPT", "FETCH ONLINE")
val cacheControl = CacheControl.Builder()
.maxAge(5, TimeUnit.SECONDS)
.build()
val response = chain.proceed(chain.request().newBuilder()
.removeHeader("Pragma")
.removeHeader("Cache-Control")
.header("Cache-Control", "public, $cacheControl")
.build())
Log.wtf("INTERCEPT", "CACHE ${response.cacheResponse} NETWORK ${response.networkResponse}")
response
} catch (e: IOException) {
Log.wtf("INTERCEPT", "FALLBACK TO CACHE ${e.message}")
val cacheControl: CacheControl = CacheControl.Builder()
.maxStale(30, TimeUnit.DAYS)
.onlyIfCached() // Use Cache if available
.build()
val offlineRequest: Request = chain.request().newBuilder()
.cacheControl(cacheControl)
.build()
val response = chain.proceed(offlineRequest)
Log.wtf("INTERCEPT", "CACHE ${response.cacheResponse} NETWORK ${response.networkResponse}")
response
}
}
}
fun create(baseUrl: String, cacheDir: File): EndpointServices {
// Inexact 150 MB of maximum cache size for a total of 4000 assets where 1MB/30 assets
// The remaining available space will be use for other cacheable requests
val cacheSize: Long = 150 * 1024 * 1024
val cache = Cache(cacheDir, cacheSize)
Log.wtf("CACHE DIRECTORY", cache.directory.absolutePath)
for (cacheUrl in cache.urls())
Log.wtf("CACHE URLS", cacheUrl)
Log.wtf("CACHE OCCUPIED/TOTAL SIZE", "${cache.size()}/${cache.maxSize()}")
/*val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY*/
val httpClient = OkHttpClient.Builder()
.cache(cache)
/*.addInterceptor(interceptor)*/
.callTimeout(10, TimeUnit.SECONDS)
.connectTimeout(10, TimeUnit.SECONDS)
.addNetworkInterceptor(interceptor())
.addInterceptor(onlineOfflineHandling())
.build()
val retrofit = Retrofit.Builder()
.addCallAdapterFactory(
RxJava2CallAdapterFactory.create()
)
.addConverterFactory(
MoshiConverterFactory.create()
)
.client(httpClient)
.baseUrl(baseUrl)
.build()
return retrofit.create(EndpointServices::class.java)
}
}
@GET("{fullPath}")
fun getExchangeItems(
@Path("fullPath", encoded = true) fullPath: String,
@Query("fields") fields: String
):
Single<ExchangeItemModel>
}
Fetching it with this
private val RetroService by lazy {
EndpointServices.create(MySingleton.assetLink, application.cacheDir)
}
RetroService.getExchangeItems(
MySingleton.exchange.replace("{ID}", assetName.trim().replace(" ", "-")),
MySingleton.exchangeField
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ result ->
// Filter the item to display
includedAsset.value =
result.data.filter { it.exchangeName != null && !it.excludedFromPrice}
excludedAsset.value =
result.data.filter { it.exchangeName != null && it.excludedFromPrice}
},
{ error ->
Log.wtf("WTF", "${error.message}")
FirebaseCrashlytics.getInstance().recordException(error)
// Only change the UI with this conditions
if ((includedAsset.value!!.isEmpty() && excludedAsset.value!!.isEmpty()) || (error is HttpException && error.code() == HttpURLConnection.HTTP_GATEWAY_TIMEOUT))
errorMessage.value = R.string.swipe_to_refresh
}
))
Case 1: No network (Wifi/Mobile Data is OFF)
-
Interceptor :
A/INTERCEPT: FALLBACK TO CACHE Unable to resolve host "some.host.com": No address associated with hostname
-
onError (error ->) is called if no cache
-
onSuccess (result ->) is called if cache is available
All Good! We show error UI to user during offline mode if no cache exist else we show the list if cache is at least available.
========================================================================
Case 2: Connected to network (Wifi/Mobile Data is ON and the network has INTERNET SERVICE)
-
onError (error ->) is called when response was failed such as 404, etc.
-
onSuccess (result ->) is called when response was success
All Good! BUT due to unknown circumstances sometimes I got
A/INTERCEPT: FALLBACK TO CACHE CANCELED
from the Interceptor
and when this happen I don't receive any callbacks neither onError nor onSuccess thus the UI for loading never ends.
========================================================================
Case 3: Connected to network (Wifi/Mobile Data is ON but the network has NO INTERNET SERVICE)
-
Interceptor :
A/INTERCEPT: FALLBACK TO CACHE Unable to resolve host "some.host.com": No address associated with hostname
-
onError (error ->) is not called
-
onSuccess (result ->) is not called
As you can see, my Interceptor
log here is just the same in our first case yet no callback has been return even a cache is available thus the UI for loading never ends again.
dependencies
implementation 'com.squareup.moshi:moshi-kotlin:1.11.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.0'
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
// Because RxAndroid releases are few and far between, it is recommended you also
// explicitly depend on RxJava's latest version for bug fixes and new features.
// (see https://github.com/ReactiveX/RxJava/releases for latest 3.x.x version)
implementation 'io.reactivex.rxjava3:rxjava:3.0.0'