简体   繁体   English

无法从缓存okHttp和retrofit加载数据

[英]Not able to load data from cache okHttp & retrofit

Here is my code where i am calling api and also define cache for okhttp with retrofit: 这是我的代码,我在这里调用api,并通过改造为okhttp定义缓存:

public class DemoPresenter {

    DemoView vDemoView;
    private Context mContext;

    public DemoPresenter(Context mcontext, DemoView vDemoView) {
        this.vDemoView = vDemoView;
        this.mContext = mcontext;
    }

    public void callAllProduct() {
        if (vDemoView != null) {
            vDemoView.showProductProgressBar();
        }


        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .cache(new Cache(mContext.getCacheDir(), 10 * 1024 * 1024)) // 10 MB
                .addInterceptor(new Interceptor() {
                    @Override
                    public okhttp3.Response intercept(Chain chain) throws IOException {
                        Request request = chain.request();
                        if (BasicUtility.isInternet(mContext)) {
                            request = request.newBuilder().header("Cache-Control", "public, max-age=" + 60).build();
                        } else {
                            request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 7).build();
                        }
                        return chain.proceed(request);
                    }
                })
                .build();


        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("www.xyz.com/data/")
                .addConverterFactory(GsonConverterFactory.create())
                .client(okHttpClient)
                .build();

        AllApi productApiService = retrofit.create(AllApi.class);
        Call<ProductData> call = productApiService.getProduct();

        try {
            call.enqueue(new Callback<ProductData>() {
                @Override
                public void onResponse(Call<ProductData> call, Response<ProductData> response) {
                    ArrayList<Product> alproducts = new ArrayList<>();
                    try {
                        alproducts = response.body().getProductData();
                        onSuccess(alproducts);
                    } catch (Exception e) {

                    }
                }
                @Override
                public void onFailure(Call<ProductData> call, Throwable t) {
                }
            });
        } catch (Exception e) {

        }
    }

    private void onSuccess(ArrayList<Product> alproducts) {
        if (vDemoView != null) {
            vDemoView.hideProductProgressBar();
            vDemoView.onProductSuccess(alproducts);
        }
    }
}

Now from my main activity i am calling this presenter class: 现在从我的主要活动我打电话给这个演示者课:

DemoPresenter mDemoPresenter = new DemoPresenter(getApplicationContext(),this);
 mDemoPresenter.callAllProduct();

Now when i run this activity with Internet connectivity then it works fine but when i disconnected internet and run this activity again then it wont load data from cache . 现在,当我使用Internet连接运行此活动时,它工作正常,但当我断开互联网并再次运行此活动,然后它不会从缓存加载数据。

How can i load this data from cache if there is not internet ? 如果没有互联网,如何从缓存加载此数据?

You can try out this : 你可以尝试这个:

public class DemoPresenter {

    DemoView vDemoView;
    private Context mContext;
    private static final String CACHE_CONTROL = "Cache-Control";
    private static final String TAG = DemoPresenter.class.getName();

    public DemoPresenter(Context mcontext, DemoView vDemoView) {
        this.vDemoView = vDemoView;
        this.mContext = mcontext;
    }

    public void callAllProduct() {
        if (vDemoView != null) {
            vDemoView.showProductProgressBar();
        }


        OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .addInterceptor( provideOfflineCacheInterceptor() )
            .addNetworkInterceptor( provideCacheInterceptor() )
            .cache( provideCache() )
            .build();


       /* OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .cache(new Cache(mContext.getCacheDir(), 10 * 1024 * 1024)) // 10 MB
                .addInterceptor(new Interceptor() {
                    @Override
                    public okhttp3.Response intercept(Chain chain) throws IOException {
                        Request request = chain.request();
                        if (BasicUtility.isInternet(mContext)) {
                            request = request.newBuilder().header("Cache-Control", "public, max-age=" + 60).build();
                        } else {
                            request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 7).build();
                        }
                        return chain.proceed(request);
                    }
                })
                .build();*/


        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("www.xyz.com/data/")
                .addConverterFactory(GsonConverterFactory.create())
                .client(okHttpClient)
                .build();

        AllApi productApiService = retrofit.create(AllApi.class);
        Call<ProductData> call = productApiService.getProduct();

        try {
            call.enqueue(new Callback<ProductData>() {
                @Override
                public void onResponse(Call<ProductData> call, Response<ProductData> response) {
                    ArrayList<Product> alproducts = new ArrayList<>();
                    try {
                        alproducts = response.body().getProductData();
                        onSuccess(alproducts);
                    } catch (Exception e) {

                    }
                }
                @Override
                public void onFailure(Call<ProductData> call, Throwable t) {
                }
            });
        } catch (Exception e) {

        }
    }

    private void onSuccess(ArrayList<Product> alproducts) {
        if (vDemoView != null) {
            vDemoView.hideProductProgressBar();
            vDemoView.onProductSuccess(alproducts);
        }
    }

    public Interceptor provideOfflineCacheInterceptor () {
        return new Interceptor()
        {
            @Override
            public Response intercept (Chain chain) throws IOException
            {
                Request request = chain.request();

                if (BasicUtility.isInternet(mContext))
                {
                    CacheControl cacheControl = new CacheControl.Builder()
                        .maxStale( 7, TimeUnit.DAYS )
                        .build();

                    request = request.newBuilder()
                        .cacheControl( cacheControl )
                        .build();
                }

                return chain.proceed( request );
            }
        };
    }




    public static Interceptor provideCacheInterceptor ()
    {
        return new Interceptor()
        {
            @Override
            public Response intercept (Chain chain) throws IOException
            {
                Response response = chain.proceed( chain.request() );

                // re-write response header to force use of cache
                CacheControl cacheControl = new CacheControl.Builder()
                    .maxAge( 1, TimeUnit.MINUTES )
                    .build();

                return response.newBuilder()
                    .header( CACHE_CONTROL, cacheControl.toString() )
                    .build();
            }
        };
    }

    private Cache provideCache ()
    {
        Cache cache = null;
        try
        {
            cache = new Cache( new File( mContext.getCacheDir(), "http-cache" ),
                10 * 1024 * 1024 ); // 10 MB
        }
        catch (Exception e)
        {
            Log.e(TAG, "Could not create Cache!");
        }
        return cache;
    }
}

Working well for me. 对我有用。

Possible errors: Because you put everything inside callAllProduct (), so you each time create new okHttpClient , and new Cache , you are not reusing your old Cache . 可能的错误:因为你把所有东西放在callAllProduct ()中,所以每次创建新的okHttpClient和新的Cache ,你都不会重用旧的Cache You functionally dependent on callAllProduct, callAllProduct depends on new okHttpClient, okHttpCient is functionality dependent on new cache. 你在功能上依赖于callAllProduct,callAllProduct依赖于新的okHttpClient,okHttpCient是依赖于新缓存的功能。 Putting okHttpClient outside your callAllProduct () body makes you depend on the same old okHttpClient in each callAllProduct call. okHttpClient放在callAllProduct ()体外部会使您在每个callAllProduct调用中依赖于相同的旧okHttpClient。 Just try this even though I also don't know how Retrofit internal caching works. 试试这个,即使我也不知道Retrofit内部缓存是如何工作的。 If it still not working, I apologise for my not working idea, but I promise, I will help you again. 如果它仍然无法正常工作,我为我的不工作的想法道歉,但我保证,我会再次帮助你。

The idea is: Each time you call callAllProduct(), you use okHttpClient request to Retrofit API layer. 这个想法是:每次调用callAllProduct()时,都会使用okHttpClient请求来改进API层。 Retrofit layer checks if it already saved the data that is associated with your okHttpClient or not? 改造层检查它是否已经保存了与您的okHttpClient相关联的数据? Each new okHttpClient instance means each new http request, thus each new id it generates for the caching of data. 每个新的okHttpClient实例意味着每个新的http请求,因此它为缓存数据生成每个新的id。 Old cache is never used because each time you are using a new instance of okHttpClient. 从不使用旧缓存,因为每次使用okHttpClient的新实例时。 Retrofit doesn't see any associated id with your okHttpRequest instance, thus, forwards the request to internet. Retrofit没有在您的okHttpRequest实例中看到任何关联的id,因此将请求转发到Internet。 Web-server responses with data. Web服务器对数据的响应。 Now, Retrofit creates new id for the cache for that successful ok HTTP client request. 现在,Retrofit为该成功的正常HTTP客户端请求的缓存创建新的id。 But when you use new okHttpClient each time, old cache id never used, thus there happens cache-miss always. 但是当你每次使用新的okHttpClient时,旧的缓存id从未使用过, 因此总会发生缓存未命中。

This below code should be outside callAllProduct() body 下面的代码应该在callAllProduct()体外

 int cacheSize = 10 * 1024 * 1024; /* 10 MB. Also try increasing cache size */
    public static Cache myCache = new Cache(getCacheDir(), cacheSize);

  OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .cache(myCache) // 10 MB
                .addInterceptor(new Interceptor() {
                    @Override
                    public okhttp3.Response intercept(Chain chain) throws IOException {
                        Request request = chain.request();
                        if (BasicUtility.isInternet(mContext)) {
                            request = request.newBuilder().header("Cache-Control", "public, max-age=" + 60).build();
                        } else {
                            request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 7).build();
                        }
                        return chain.proceed(request);
                    }
                })
                .build();

Now, your callAllProduct() becomes as shown below: . 现在,您的callAllProduct()变为如下所示: This guarantees you use same okHttpClient all everytime you call callAllProduct(). 这可以保证每次调用callAllProduct()时都使用相同的okHttpClient。

public void callAllProduct() {
        if (vDemoView != null) {
            vDemoView.showProductProgressBar();
        }

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("www.xyz.com/data/")
                .addConverterFactory(GsonConverterFactory.create())
                .client(okHttpClient)
                .build();

        AllApi productApiService = retrofit.create(AllApi.class);
        Call<ProductData> call = productApiService.getProduct();

        try {
            call.enqueue(new Callback<ProductData>() {
                @Override
                public void onResponse(Call<ProductData> call, Response<ProductData> response) {
                    ArrayList<Product> alproducts = new ArrayList<>();
                    try {
                        alproducts = response.body().getProductData();
                        onSuccess(alproducts);
                    } catch (Exception e) {

                    }
                }
                @Override
                public void onFailure(Call<ProductData> call, Throwable t) {
                }
            });
        } catch (Exception e) {

        }
    }

Your interceptor should check for connectivity, and set cacheHeaderValue accordingly, In this example it uses a method isNetworkAvailable to do this: 您的拦截器应检查连接,并相应地设置cacheHeaderValue。在此示例中,它使用方法isNetworkAvailable来执行此操作:

okClient.interceptors().add(
                new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Request originalRequest = chain.request();
                        String cacheHeaderValue = isNetworkAvailable(context)
                                ? "public, max-age=2419200"
                                : "public, only-if-cached, max-stale=2419200" ;
                        Request request = originalRequest.newBuilder().build();
                        Response response = chain.proceed(request);
                        return response.newBuilder()
                                .removeHeader("Pragma")
                                .removeHeader("Cache-Control")
                                .header("Cache-Control", cacheHeaderValue)
                                .build();
                    }
                }
        );
        okClient.networkInterceptors().add(
                new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Request originalRequest = chain.request();
                        String cacheHeaderValue = isNetworkAvailable(context)
                                ? "public, max-age=2419200"
                                : "public, only-if-cached, max-stale=2419200" ;
                        Request request = originalRequest.newBuilder().build();
                        Response response = chain.proceed(request);
                        return response.newBuilder()
                                .removeHeader("Pragma")
                                .removeHeader("Cache-Control")
                                .header("Cache-Control", cacheHeaderValue)
                                .build();
                    }
                }
        );

You need to add an offline Cache Interceptor for OkHttp to cache the response for offline use. 您需要为OkHttp添加一个脱机Cache Interceptor来缓存响应以供离线使用。 You can also cache data for a minute and if the request is send within a minute, the data from the cache is used. 您还可以缓存数据一分钟,如果请求在一分钟内发送,则使用缓存中的数据。

/**
 * Interceptor to cache data and maintain it for four weeks.
 *
 * If the device is offline, stale (at most four weeks old)
 * response is fetched from the cache.
 */
private static class OfflineResponseCacheInterceptor implements Interceptor {
    @Override
    public okhttp3.Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        if (!UtilityMethods.isNetworkAvailable()) {
            request = request.newBuilder()
                    .header("Cache-Control",
                      "public, only-if-cached, max-stale=" + 2419200)
                    .build();
        }
        return chain.proceed(request);
    }
}

Follow this tutorial to understand more about caching data - https://krtkush.github.io/2016/06/01/caching-using-okhttp-part-1.html Also this Stack Overflow answer Can Retrofit with OKHttp use cache data when offline 按照本教程了解有关缓存数据的更多信息 - https://krtkush.github.io/2016/06/01/caching-using-okhttp-part-1.html此Stack Overflow答案可以使用OKHttp进行改造时使用缓存数据离线

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM