Doing network caching with Retrofit 2 in Android
The following example will walk through the steps for doing caching with Retrofit 2. The example here will be using an Github API for getting a Github user account information. The big picture is to make a request to an api, the first request will get the response from the server, the following requests within 1 minute will be getting the response from cached data.
1. gradle dependencies
// rxandroid implementation 'io.reactivex.rxjava2:rxandroid:2.0.1' // Retrofit 2 for network tasks implementation 'com.squareup.retrofit2:retrofit:2.3.0' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' implementation 'com.squareup.retrofit2:converter-gson:2.3.0' implementation 'com.squareup.okhttp3:logging-interceptor:3.9.0' implementation 'com.google.code.gson:gson:2.8.2'
2. Create a pojo object. This is to hold the Github account information which will be obtained when making the network request to Github api. GithubAccount.kt
import com.google.gson.annotations.SerializedName data class GithubAccount( @SerializedName("login") var login : String = "", @SerializedName("id") var id : Int = 0, @SerializedName("avatar_url") var avatarUrl : String = "", @SerializedName("created_at") var createdAt : String = "", @SerializedName("updated_at") var updatedAt : String = "")
3. Create a Retrofit network interface. GithubApi.kt
import io.reactivex.Observable import retrofit2.Response import retrofit2.http.GET import retrofit2.http.Path interface GithubApi { @GET("/users/{username}") fun getGithubAccountObservable(@Path("username") username: String): Observable> }
4. Create the cache object. The cache size is set to 10MB, which means old cached data will be over written with newer data. getCacheDir()
is a method from Activity class and it returns the Android’s default cache directory.
val cacheSize = 10 * 1024 * 1024 // 10 MB val httpCacheDirectory = File(getCacheDir(), "http-cache") val cache = Cache(httpCacheDirectory, cacheSize.toLong())
5. Create the network cache interceptor. Setting the max age to 1 minute, which means the cache will be valid for 1 minute. For example, the first request will be getting response from the server, and the following request made within 1 minute of the first request will be getting response from the cache.
val networkCacheInterceptor = Interceptor { chain -> val response = chain.proceed(chain.request()) var cacheControl = CacheControl.Builder() .maxAge(1, TimeUnit.MINUTES) .build() response.newBuilder() .header("Cache-Control", cacheControl.toString()) .build() }
6. Create the logging interceptor. This is not required for the cache, but it’s nice to have some logging statements when debugging.
val loggingInterceptor = HttpLoggingInterceptor() loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
7. Create the httpClient. Configure it with cache, network cache interceptor and logging interceptor
val httpClient = OkHttpClient.Builder() .cache(cache) .addNetworkInterceptor(networkCacheInterceptor) .addInterceptor(loggingInterceptor) .build()
8. Create the Retrofit with the httpClient.
val retrofit = Retrofit.Builder() .baseUrl("https://api.github.com/") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .client(httpClient) .build()
9. Build the gitHubApi with Retrofit and do the network request. The result will be shown on a toast message. There is also a logging telling you whether the response was from the server or cache.
val githubApi = retrofit.create(GithubApi::class.java) githubApi.getGithubAccountObservable("google") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(object : DisposableObserver>() { override fun onNext(response: Response ) { if (response.raw().cacheResponse() != null) { Log.d("Network", "response came from cache") } if (response.raw().networkResponse() != null) { Log.d("Network", "response came from server") } Toast.makeText(applicationContext, response.body().toString(), Toast.LENGTH_SHORT).show() } override fun onComplete() {} override fun onError(e: Throwable) { e.printStackTrace() } })
Step 4 to 9 in one Activity class.
import android.content.Intent import android.os.Bundle import android.support.v7.app.AppCompatActivity import android.util.Log import android.view.View import android.widget.Toast import com.example.myapplication.data.GithubAccount import com.example.myapplication.data.GithubApi import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.observers.DisposableObserver import io.reactivex.schedulers.Schedulers import okhttp3.Cache import okhttp3.CacheControl import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Response import retrofit2.Retrofit import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory import retrofit2.converter.gson.GsonConverterFactory import java.io.File import java.util.concurrent.TimeUnit class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) loadGithubAccount() } fun loadGithubAccount() { // Create a cache object val cacheSize = 10 * 1024 * 1024 // 10 MB val httpCacheDirectory = File(getCacheDir(), "http-cache") val cache = Cache(httpCacheDirectory, cacheSize.toLong()) // create a network cache interceptor, setting the max age to 1 minute val networkCacheInterceptor = Interceptor { chain -> val response = chain.proceed(chain.request()) var cacheControl = CacheControl.Builder() .maxAge(1, TimeUnit.MINUTES) .build() response.newBuilder() .header("Cache-Control", cacheControl.toString()) .build() } // Create the logging interceptor val loggingInterceptor = HttpLoggingInterceptor() loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY // Create the httpClient, configure it // with cache, network cache interceptor and logging interceptor val httpClient = OkHttpClient.Builder() .cache(cache) .addNetworkInterceptor(networkCacheInterceptor) .addInterceptor(loggingInterceptor) .build() // Create the Retrofit with the httpClient val retrofit = Retrofit.Builder() .baseUrl("https://api.github.com/") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .client(httpClient) .build() // Build the gitHubApi with Retrofit and do the network request val githubApi = retrofit.create(GithubApi::class.java) githubApi.getGithubAccountObservable("google") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(object : DisposableObserver>() { override fun onNext(response: Response ) { if (response.raw().cacheResponse() != null) { Log.d("Network", "response came from cache") } if (response.raw().networkResponse() != null) { Log.d("Network", "response came from server") } Toast.makeText(applicationContext, response.body().toString(), Toast.LENGTH_SHORT).show() } override fun onComplete() {} override fun onError(e: Throwable) { e.printStackTrace() } }) } }
References:
https://futurestud.io/tutorials/retrofit-2-activate-response-caching-etag-last-modified
https://stackoverflow.com/questions/23429046/can-retrofit-with-okhttp-use-cache-data-when-offline
https://stackoverflow.com/questions/45904054/retrofit-2-okhttpclient-caching-not-working
Search within Codexpedia
Search the entire web