Whenever there is an error like network related errors, we will use the last cached data from the client instead. It works but only when not connected to any network. If a client is connected to any network where internet service is not really available the callbacks is no longer working.
So far I do not know which side this error came from base on this 3 libraries.
Here we are using YouTube API as a sample.
interface EndpointServices {
companion object {
private fun interceptor(): Interceptor {
return Interceptor { chain ->
val request: Request = chain.request()
val originalResponse: Response = chain.proceed(request)
val cacheControlStatus: String? = originalResponse.header("Cache-Control")
if (cacheControlStatus == null || cacheControlStatus.contains("no-store") || cacheControlStatus.contains(
"no-cache") ||
cacheControlStatus.contains("must-revalidate") || cacheControlStatus.contains("max-stale=0")
) {
Log.wtf("INTERCEPT", "ORIGINAL CACHE-CONTROL: $cacheControlStatus")
} else {
Log.wtf("INTERCEPT",
"ORIGINAL : CACHE-CONTROL: $cacheControlStatus")
}
Log.wtf("INTERCEPT",
"OVERWRITE CACHE-CONTROL: ${request.cacheControl} | CACHEABLE? ${
CacheStrategy.isCacheable(originalResponse,
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: Exception) {
Log.wtf("INTERCEPT", "FALLBACK TO CACHE ${e.message}")
val cacheControl: CacheControl = CacheControl.Builder()
.maxStale(1, 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, context: Context): EndpointServices {
// Inexact 150 MB of maximum cache size for a total of 4000 assets where about 1MB/30 assets
// The remaining available space will be use for other cacheable requests
val cacheSize: Long = 150 * 1024 * 1024
val cache = Cache(context.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("search")
fun getVideoItems(
@Query("key") key: String,
@Query("part") part: String,
@Query("maxResults") maxResults: String,
@Query("order") order: String,
@Query("type") type: String,
@Query("channelId") channelId: String,
):
Single<VideoItemModel>
}
A/CACHE DIRECTORY: /data/data/com.appname.app/cache
A/CACHE URLS: www.api.com
A/CACHE OCCUPIED/TOTAL SIZE: 228982 157286400
A/INTERCEPT: FETCH ONLINE
A/INTERCEPT: FALLBACK TO CACHE Unable to resolve host "api.com": No address associated with hostname
A/INTERCEPT: CACHE Response{protocol=http/1.1, code=200, message=, url=https://api.com} NETWORK null
A/RESPONSE: VideoItemModel(.....) WORKING!
JUST CONNECTED TO A NETWORK BUT REALLY NO INTERNET SERVICE (Wi-Fi/Mobile Data ON)
A/CACHE DIRECTORY: /data/data/com.appname.app/cache
A/CACHE URLS: www.api.com
A/CACHE OCCUPIED/TOTAL SIZE: 228982 157286400
A/INTERCEPT: FETCH ONLINE
A/INTERCEPT: FALLBACK TO CACHE Unable to resolve host "api.com": No address associated with hostname
???WHERE IS THE CALLBACK JUST LIKE THE PREVIOUS ONE???
Also worth mentioning that neither of the line
Log.wtf("INTERCEPT", "CACHE ${response.cacheResponse} NETWORK ${response.networkResponse}") is being called on this last scenario.
Whenever there is an error like network related errors, we will use the last cached data from the client instead. It works but only when not connected to any network. If a client is connected to any network where internet service is not really available the callbacks is no longer working.
So far I do not know which side this error came from base on this 3 libraries.
Here we are using YouTube API as a sample.
MainActivity
FLOW BASED ON LOGS
WHEN ONLINE
COMPLETELY OFFLINE (Wi-Fi/Mobile Data OFF)
JUST CONNECTED TO A NETWORK BUT REALLY NO INTERNET SERVICE (Wi-Fi/Mobile Data ON)
Also worth mentioning that neither of the line
Log.wtf("INTERCEPT", "CACHE ${response.cacheResponse} NETWORK ${response.networkResponse}")
is being called on this last scenario.Dependencies