Retrofit2 - 如何仅缓存某些 API 调用

j2e*_*nue 3 android caching http-headers retrofit2

我只想缓存某些呼叫而不是全部。这是我现在拥有的代码,但它将影响所有改造调用:

int cacheSize = 10 * 1024 * 1024; // 10 MB  
Cache cache = new Cache(getCacheDir(), cacheSize);

OkHttpClient okHttpClient = new OkHttpClient.Builder()
    .cache(cache)
    .build();

Retrofit.Builder builder = new Retrofit.Builder()  
    .baseUrl("http://10.0.2.2:3000/")
    .client(okHttpClient)
    .addConverterFactory(GsonConverterFactory.create());

Retrofit retrofit = builder.build(); 
Run Code Online (Sandbox Code Playgroud)

当我只想为该调用进行缓存时,是否可以在标头中放入一些内容?

例如:

// 我想我足以在标题中添加一些内容。我只想缓存客户端(android)端的调用。所以我想改造可以记住响应并缓存它以供下一次调用,但我不希望它用于我的所有调用,只需要我想要的那些,也许1或2。其余的可以一直连接到服务器。这是如何实现的?

@Headers("Cache-Control:????XXXXX) //is it possible this way ?, how ?
@GET("getBusiness.action")// Store information 
Call<RestaurantInfoModel> getRestaurantInfo(@Query("userId") String userId,@Query("businessId") String businessId);
Run Code Online (Sandbox Code Playgroud)

更新

这是我现在尝试过的:

这是我构建 okhttpclient 的方法:

final OkHttpClient.Builder builder = new OkHttpClient.Builder();
int cacheSize = 10 * 1024 * 1024; // 10 MB
Cache cache = new Cache(getCacheDir(), cacheSize);
if (BuildConfig.RETROFIT_LOG_ALL) {
    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
    logging.setLevel(HttpLoggingInterceptor.Level.BODY);
    builder.addInterceptor(logging);
}
builder.cache(cache);
return builder.build();
Run Code Online (Sandbox Code Playgroud)

后来我将它添加到改造中,它可以工作,但似乎不能与缓存一起使用。让我们看看没有向 okhttp 添加缓存的标头响应:

Content-Type: application/json; charset=utf-8
06-18 17:54:58.044 11233-11735/com.mobile.myapp.staging D/OkHttp: Connection: close
06-18 17:54:58.044 11233-11735/com.mobile.myapp.staging D/OkHttp: Transfer-Encoding: chunked
06-18 17:54:58.044 11233-11735/com.mobile.myapp.staging D/OkHttp: X-Powered-By: Express
06-18 17:54:58.044 11233-11735/com.mobile.myapp.staging D/OkHttp: Vary: Origin
06-18 17:54:58.044 11233-11735/com.mobile.myapp.staging D/OkHttp: Access-Control-Allow-Credentials: true
06-18 17:54:58.044 11233-11735/com.mobile.myapp.staging D/OkHttp: Cache-Control: public, max-age=0
06-18 17:54:58.044 11233-11735/com.mobile.myapp.staging D/OkHttp: ETag: W/"39ba-G9evSsiVDp9GAVGu1Mk4ag"
06-18 17:54:58.044 11233-11735/com.mobile.myapp.staging D/OkHttp: Date: Sun, 18 Jun 2017 10:54:58 GMT
Run Code Online (Sandbox Code Playgroud)

这是我所做的测试,看看缓存是否有效。我使用这个向 api 发出了请求:

public interface CountriesApi {
    @GET("countries")
    @Headers({"Content-Type:application/json"})
    Observable<List<CountryModel>> getCountries();
}
Run Code Online (Sandbox Code Playgroud)

然后我关闭了 Android 设备上的互联网,并尝试在应用程序中再次进行相同的调用。但相反,我得到了改造,抱怨没有网络连接。相反,它应该刚刚从缓存中获取。知道出什么问题了吗?

java.net.SocketException: Network is unreachable at
java.net.PlainSocketImpl.socketConnect(Native Method) at
java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:334) at
java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:196)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:178) at
java.net.SocksSocketImpl.connect(SocksSocketImpl.java:356) at
java.net.Socket.connect(Socket.java:586) at
okhttp3.internal.platform.AndroidPlatform.connectSocket(AndroidPlatform.java:63)
at
okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.java:223)
at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:149)
at
okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:192)
at
okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:121)
at
okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:100)
at
okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)
at
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
at
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at
okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:120)
at
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at
okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:211)
at
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at
com.mobile.retrofit.NetworkSessionManager.intercept(NetworkSessionManager.java:38)
at
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185) at
okhttp3.RealCall.execute(RealCall.java:69) at
retrofit2.OkHttpCall.execute(OkHttpCall.java:180) at
retrofit2.adapter.rxjava2.CallExecuteObservable.subscribeActual(CallExecuteObservable.java:41)
at io.reactivex.Observable.subscribe(Observable.java:10842) at
retrofit2.adapter.rxjava2.BodyObservable.subscribeActual(BodyObservable.java:34)
at io.reactivex.Observable.subscribe(Observable.java:10842) at
io.reactivex.internal.operators.observable.ObservableFlatMap.subscribeActual(ObservableFlatMap.java:55)
at io.reactivex.Observable.subscribe(Observable.java:10842) at
io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96)
at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:452) at
io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:61)
at
io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:52)
at java.util.concurrent.FutureTask.run(FutureTask.java:237) at
java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272)
at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761) 
Run Code Online (Sandbox Code Playgroud)

Vla*_*lad 6

使用自定义注释

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class Cacheable

class MainInterceptor : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val builder = request.newBuilder()
        request.tag(Invocation::class.java)?.let {
            if (!it.method().isAnnotationPresent(Cacheable::class.java)) {
                builder.cacheControl(CacheControl.Builder()
                    .noStore()
                    .build())
                return chain.proceed(builder.build())
            }
            try {
                builder.cacheControl(CacheControl.FORCE_NETWORK)
                return chain.proceed(builder.build())
            } catch (e: Throwable) {
                e.printStackTrace()
            }
            builder.cacheControl(CacheControl.Builder()
                .maxStale(Int.MAX_VALUE, TimeUnit.DAYS)
                .build())
        }
        return chain.proceed(builder.build())
    }
}


interface ServerApi {

    @Cacheable
    @GET("some_path")
    fun getSmth(): Call<ResponseBody>
}
Run Code Online (Sandbox Code Playgroud)

不要迷失给okhttp实例添加缓存

OkHttpClient.Builder()
    .cache(Cache(context.cacheDir, 10 * 1024 * 1024L))
    .addInterceptor(MainInterceptor())
//...
Run Code Online (Sandbox Code Playgroud)