Retrofit2 - Cache
공식문서
캐시를 사용하는 이유
Caches HTTP and HTTPS responses to the filesystem so they may be reused, saving time and bandwidth.
http 및 https response를 파일 시스템에 캐시하여 재사용할 수 있어 시간과 대역폭이 절약된다.
캐시 최적화
- 캐시 효율성을 측정하기 위해 해당 클래스는 세가지 통계를 추적한다
- 요청 수
- 캐시가 생성 된 이후 발행 된 HTTP 요청 수
- 네트워크 수
- 네트워크 사용이 필요한 요청 수
- 적중 수
- 캐시에서 응답을 제공한 요청 수
- 요청 수
- 조건부 캐시
- 오래된 reponse 사본이 포함 된 경우 클라이언트는 조건부 GET
- 그 다음 서버는 변경된 경우 업데이트 된 response를 보내거나 클라이언트의 복사본이 여전히 유효한 경우 짧은 ‘not modified’ 라는 response을 보낸다
- 이러한 짧은 response를 통해서 네트워크 수와 hit수를 모두 증가시키게 된다
네트워크 response 강제
- 사용자가 새로고침 버튼을 클린 한 후오 같은 일부 상황에서는 캐시를 건너 뛰고 서버에서 직접 데이터를 가져와야 할 수 있다
- 이 경우
no-cache
를 추가한다
1
2
3
4
val request = Request.Builder()
.cacheControl(CacheControl.Builder().noCache().build())
.url(url)
.build()
캐시 response 강제
- 리소스를 즉시 사용할 수 있을 때 이 리소스를 바로 보여주고자 하는 경우가 있다
- 이때 캐시 response를 강제하게 되면 최신 데이터가 다운로드 되기를 기다리는 동안 프로그램이 무언가를 표시할 수 있도록 사용할 수 있다
- request를 로컬로 캐시 된 리소스로 제한하려면
only-if-cached
지시문을 추가한다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
val request = Request.Builder()
.cacheControl(CacheCongtrol.Builder()
.onlyIfCached()
.build()
)
.url(url)
.build()
val forceCacheResponse = client.newCall(request).execute()
if (forceCacheResponse.code != 504) {
// resource was cached
} else {
// resource was not cached
}
- response가 없는 상황보다 오래된 response가 있는 상황에서 더 잘 작동한다
- 오래된 캐시 response 를 허용하려면 최대 정지 상태(초)와 함께 최대 정지 상태 지시어를 사용해야 한다
1
2
3
4
5
6
7
val request = Request.Builder()
.cacheControl(CacheControl.BuildeR()
.maxStale(365, TimeUnit.DAYS)
.build()
)
.url(url)
.build()
- 테스트
- 테스트용 api 서버에 요청해서 테스트
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class ApiClient {
private val testUrl = "<https://jsonplaceholder.typicode.com>"
fun getClient(): Retrofit {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val client = OkHttpClient().newBuilder()
/* 10MB */
val cacheSize = (10 * 1024 * 1024).toLong()
/* cache */
val cache = Cache(File(BardBase.ApplicationContext().cacheDir, "http"), cacheSize)
client
.cache(cache)
.addNetworkInterceptor(networkInterceptor())
.addInterceptor(interceptor)
return Retrofit.Builder()
.baseUrl(testUrl)
.client(client.build())
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addConverterFactory(GsonConverterFactory.create())
.build()
}
private fun networkInterceptor(): Interceptor {
return Interceptor { chain ->
val cacheControl = CacheControl.Builder()
// 캐시 유효 시간
.maxAge(30, TimeUnit.SECONDS)
.build()
val response: Response = chain.proceed(chain.request())
response.newBuilder()
.removeHeader("Pragma")
.removeHeader("Cache-Control")
.header("Cache-Control", cacheControl.toString())
.build()
}
}
}
실제로 내가 원했던, 생각했던 Cache라는 기능은 아니였지만, 알아두면 좋을 것 같은 Retrofit2의 Cache 기능이였다.