Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/201.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/61.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 改型2-如何仅缓存某些API调用_Android_Caching_Http Headers_Retrofit2 - Fatal编程技术网

Android 改型2-如何仅缓存某些API调用

Android 改型2-如何仅缓存某些API调用,android,caching,http-headers,retrofit2,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 builde

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

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(); 
当我只想对那个调用进行缓存时,是否可以在标题中添加一些内容

例如:

//我在想我可以在头球上放些东西。我只想缓存客户端(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);
稍后,我将其添加到改造中,它可以工作,但似乎不能与缓存一起工作。让我们在不向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
下面是我做的测试,看看缓存是否工作。我使用以下命令向api发出请求:

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

是OkHttp库完成了这个任务,它主要由响应头驱动。您应该检查它们并验证是否缓存了API响应。应该有助于调试

要检查是否正在发生网络请求,请将HttpLoggingInterceptor用作网络侦听器,而不是应用程序侦听器(请参阅):

当请求由缓存处理时,不会调用网络侦听器

下一步将视情况而定

1) 所有API请求都被缓存

然后,您应该能够对您不想缓存的方法使用annotation@Headers(“缓存控制:无缓存”)

2) 未缓存所有API请求


然后,您可以更改API(添加适当的缓存头),也可以为OkHttp实现网络拦截器,该拦截器将修改响应(头)。您可以在这个响应中找到一些灵感:

是OkHttp库完成了这个任务,它主要由响应头驱动。您应该检查它们并验证是否缓存了API响应。应该有助于调试

要检查是否正在发生网络请求,请将HttpLoggingInterceptor用作网络侦听器,而不是应用程序侦听器(请参阅):

当请求由缓存处理时,不会调用网络侦听器

下一步将视情况而定

1) 所有API请求都被缓存

然后,您应该能够对您不想缓存的方法使用annotation@Headers(“缓存控制:无缓存”)

2) 未缓存所有API请求


然后,您可以更改API(添加适当的缓存头),也可以为OkHttp实现网络拦截器,该拦截器将修改响应(头)。您可以从这个回答中找到一些灵感:

使用自定义注释

@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>
}

使用自定义注释

@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>
}

我也在寻找同样的解决方案,他们提出了这个解决方案:

首先创建一个Singleton改装类:

public class RetrofitClient {

private static final String BASE_URL = "BASE URL HERE...";

private static RetrofitClient mInstance;

private final Retrofit retrofit;
private final Retrofit retrofitCache;

private static final String HEADER_CACHE_CONTROL = "Cache-Control";
private static final String HEADER_PRAGMA = "Pragma";
int cacheSize = 10 * 1024 * 1024; // 10 MB

public static synchronized RetrofitClient getInstance(Context context) {
    if (mInstance == null) {
        mInstance = new RetrofitClient(context);
    }
    return mInstance;
}

private RetrofitClient(Context context) {
    retrofit = new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    retrofitCache = new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .client(okHttpClient(context))
            .addConverterFactory(GsonConverterFactory.create())
            .build();

}

public ApiInterface getApi() {
    return retrofit.create(ApiInterface.class);
}

public ApiInterface getCachedApi() {
    return retrofitCache.create(ApiInterface.class);
}

Interceptor networkInterceptor() {
    return chain -> {
        CacheControl cacheControl = new CacheControl.Builder()
                .maxAge(5, TimeUnit.MINUTES) //cache validity time
                .build();

        Response response = chain.proceed(chain.request());

        return response.newBuilder()
                .removeHeader(HEADER_PRAGMA)
                .removeHeader(HEADER_CACHE_CONTROL)
                .header(HEADER_CACHE_CONTROL, cacheControl.toString())
                .build();
    };
}

OkHttpClient okHttpClient(Context context) {
    Cache cache = new Cache(context.getCacheDir(), cacheSize);

    return new OkHttpClient.Builder()
            .cache(cache)
            .addNetworkInterceptor(networkInterceptor())
            .build();
}
}

然后像这样创建API接口:

public interface ApiInterface {

    @GET("apiEndPoint")
    Call<ResponseBody> getData();
}
//Without Cache
Call<AboutResponse> call =
            RetrofitClient.getInstance(this)
                    .getApi()
                    .getAbout();

/With Cache
Call<AboutResponse> call =
            RetrofitClient.getInstance(this)
                    .getCachedApi()
                    .getAbout();

我也在寻找同样的解决方案,他们提出了这个解决方案:

首先创建一个Singleton改装类:

public class RetrofitClient {

private static final String BASE_URL = "BASE URL HERE...";

private static RetrofitClient mInstance;

private final Retrofit retrofit;
private final Retrofit retrofitCache;

private static final String HEADER_CACHE_CONTROL = "Cache-Control";
private static final String HEADER_PRAGMA = "Pragma";
int cacheSize = 10 * 1024 * 1024; // 10 MB

public static synchronized RetrofitClient getInstance(Context context) {
    if (mInstance == null) {
        mInstance = new RetrofitClient(context);
    }
    return mInstance;
}

private RetrofitClient(Context context) {
    retrofit = new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    retrofitCache = new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .client(okHttpClient(context))
            .addConverterFactory(GsonConverterFactory.create())
            .build();

}

public ApiInterface getApi() {
    return retrofit.create(ApiInterface.class);
}

public ApiInterface getCachedApi() {
    return retrofitCache.create(ApiInterface.class);
}

Interceptor networkInterceptor() {
    return chain -> {
        CacheControl cacheControl = new CacheControl.Builder()
                .maxAge(5, TimeUnit.MINUTES) //cache validity time
                .build();

        Response response = chain.proceed(chain.request());

        return response.newBuilder()
                .removeHeader(HEADER_PRAGMA)
                .removeHeader(HEADER_CACHE_CONTROL)
                .header(HEADER_CACHE_CONTROL, cacheControl.toString())
                .build();
    };
}

OkHttpClient okHttpClient(Context context) {
    Cache cache = new Cache(context.getCacheDir(), cacheSize);

    return new OkHttpClient.Builder()
            .cache(cache)
            .addNetworkInterceptor(networkInterceptor())
            .build();
}
}

然后像这样创建API接口:

public interface ApiInterface {

    @GET("apiEndPoint")
    Call<ResponseBody> getData();
}
//Without Cache
Call<AboutResponse> call =
            RetrofitClient.getInstance(this)
                    .getApi()
                    .getAbout();

/With Cache
Call<AboutResponse> call =
            RetrofitClient.getInstance(this)
                    .getCachedApi()
                    .getAbout();

如何从日志中判断它是否是缓存副本?我想如果我切断互联网,我可以很容易地判断出来,但改装引发了一个异常,即没有网络连接,我看不到或没有得到任何api响应。我还注意到我的最大年龄是0,这很重要。我让okhttpclient根据我的帖子,使用构建器中的缓存对象配置缓存。您可以将日志拦截器添加为网络拦截器(Ob的构建器对象上的addNetworkInterceptor)。只有在访问网络时才会调用网络拦截器,所以缓存的响应不会显示在日志中。我将扩展响应以包含此内容。是的,API发送的Cache Control:max age=0标头非常重要,每次都会强制OkHttp执行网络请求。感谢您对网络拦截器的评论。我必须做两个。一个applicationinterceptor和一个networkinterceptor,并根据我在请求生命周期中需要执行的操作分别添加它们。感谢您的提示,日志记录不会显示在缓存响应上。最后,我添加了一个改进的动态标题,以便随时可以控制缓存。就像这篇文章解释的那样,我如何从日志中判断它是否是缓存副本?我认为如果我切断互联网,我可以很容易地判断出来,但改装引发了一个例外,即没有网络连接,我看不到或没有得到任何api响应。我还注意到我的最大年龄是0,这很重要。我让okhttpclient根据我的帖子,使用构建器中的缓存对象配置缓存。您可以将日志拦截器添加为网络拦截器(Ob的构建器对象上的addNetworkInterceptor)。只有在访问网络时才会调用网络拦截器,所以缓存的响应不会显示在日志中。我将扩展响应以包含此内容。是的,API发送的Cache Control:max age=0标头非常重要,每次都会强制OkHttp执行网络请求。感谢您对网络拦截器的评论。我必须做两个。一个applicationinterceptor和一个networkinterceptor,并根据我在请求生命周期中需要执行的操作分别添加它们。感谢您的提示,日志记录不会显示在缓存响应上。最后,我添加了一个改进的动态标题,以便随时可以控制缓存。就像这篇文章解释的一样,我想你应该首先检查并实现上面提到的拦截器。我想你应该首先检查并实现上面提到的拦截器。我希望避免反射…这是一个有趣的想法。我希望避免反射…这是一个有趣的想法。