简体   繁体   English

使用RetroHTTP 2.0和带有okhttp3的rxjava 2进行缓存

[英]Caching with retrofit 2.0 and rxjava 2 with okhttp3

I'm trying to perform offline cashing when internet connection is lost so that i can display data from cache . 我正在尝试在互联网连接丢失时执行脱机兑现,以便可以显示缓存中的数据。 here is what I've done till now . 这是我到目前为止所做的。 my question is how can make my observable return the cached arraylist of data instead of just returning error? 我的问题是如何使我的可观察对象返回缓存的数据数组列表,而不只是返回错误? my service generator : 我的服务生成器:

public class ServiceGenerator {

public static final String API_BASE_URL = UrlManager.BASE_URL_API;
private static final String CACHE_CONTROL = "Cache-Control";



private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder()
        .connectTimeout(60, TimeUnit.SECONDS)
        .writeTimeout(60,TimeUnit.SECONDS)
        .readTimeout(60,TimeUnit.SECONDS);

private static Gson gson = new GsonBuilder()

private static Retrofit.Builder builder =
        new Retrofit.Builder()
                .baseUrl(API_BASE_URL)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()));
private static Retrofit retrofit;

public static Gson getGson() {
    return gson;
}

public static void setup() {

    httpClient.addInterceptor(provideOfflineCacheInterceptor());
    httpClient.addInterceptor(new AddCookiesInterceptor()); // VERY VERY IMPORTANT
    httpClient.addInterceptor(new ReceivedCookiesInterceptor()); // VERY VERY IMPORTANT
    httpClient.addInterceptor( provideHttpLoggingInterceptor() );
    httpClient.addNetworkInterceptor(new StethoInterceptor());// Stetho
    httpClient.addNetworkInterceptor(provideCacheInterceptor());
    httpClient.cache(provideCache());
    OkHttpClient client = httpClient.build();
    retrofit = builder.client(client).build();
}

public static <S> S createService(Class<S> serviceClass) {
    return createService(serviceClass, null);
}

public static <S> S createService(Class<S> serviceClass, final String authToken) {
    if (authToken != null) {
        httpClient.addInterceptor(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request original = chain.request();

                // Request customization: add request headers
                Request.Builder requestBuilder = original.newBuilder()
                        .header("Authorization", authToken)
                        .method(original.method(), original.body());

                Request request = requestBuilder.build();
                return chain.proceed(request);
            }
        });
    }


    return retrofit.create(serviceClass);
}

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(2, TimeUnit.MINUTES)
                    .build();

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

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

            if (!UruzApplication.hasNetwork()) {
                CacheControl cacheControl = new CacheControl.Builder()
                        .maxStale(7, TimeUnit.DAYS)
                        .build();

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

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

private static Cache provideCache() {
    Cache cache = null;
    try {
        cache = new Cache(new File(UruzApplication.getInstance().getCacheDir(), "http-cache"),
                10 * 1024 * 1024); // 10 MB
    } catch (Exception e) {
        Timber.e(e, "Could not create Cache!");
    }
    return cache;
}
private static HttpLoggingInterceptor provideHttpLoggingInterceptor ()
{
    HttpLoggingInterceptor httpLoggingInterceptor =
            new HttpLoggingInterceptor( new HttpLoggingInterceptor.Logger()
            {
                @Override
                public void log (String message)
                {
                    Timber.d( message );
                }
            } );
    httpLoggingInterceptor.setLevel( true ? HEADERS : NONE );
    return httpLoggingInterceptor;
}

} }

my observer : 我的观察者:

 public static Observable<List<WeekDietPlan>>
fetchPackageWeeksDaysDietPlan(int traineeId) {

    DietService requestService = ServiceGenerator.createService(DietService.class);
    return requestService.getPackageWeekDaysDietPlan(UrlManager.getTraineeDietPackageDetailsUrl(),
            traineeId)
            .flatMap(new Function<JsonElement, Observable<List<WeekDietPlan>>>() {
                @Override
                public Observable<List<WeekDietPlan>> apply(JsonElement jsonElement) throws Exception {
                    JsonObject asJsonObject = jsonElement.getAsJsonObject();
                    String result = asJsonObject.get(UrlManager.ResultTypes.RESULT).getAsString();

                    Timber.d(TAG, "result Tag" + result);

                    if (UrlManager.ResultTypes.isError(result) || UrlManager.ResultTypes.isFailure(result)) {
                        String errorMessage = asJsonObject.get(UrlManager.ResultTypes.RESULT_ERROR_MESSAGE).getAsString();
                        return Observable.error(new Exception(errorMessage));
                    }


                    if (UrlManager.ResultTypes.isSucess(result)) {


                        if (!GsonHelper.isNull(asJsonObject.get(UrlManager.ResultTypes.RESULT_DATA)) && asJsonObject.get(UrlManager.ResultTypes.RESULT_DATA).isJsonArray()) {
                            return Observable.just(WeekDietPlan.PackageDietWeekDaysListParser.fromJsonElement(asJsonObject.getAsJsonArray(UrlManager.ResultTypes.RESULT_DATA)));
                        } else {
                            return Observable.error(new Exception("Data is empty"));
                        }


                    }

                    if (UrlManager.ResultTypes.isLogin(result)) {
                        return Observable.error(new SessionTimeoutException());
                    }


                    return Observable.error(new Exception("Unkown Tag"));
                }
            })
            .observeOn(AndroidSchedulers.mainThread());

}

my api call : 我的api调用:

private void retrievePackageWeekDaysPlan() {
    hideConnectionErrorLayout();
    if (!swipRefreshLayout_reLoad.isRefreshing()) {
        swipRefreshLayout_reLoad.setRefreshing(true);
    }

    DietNetworkCall.fetchPackageWeeksDaysDietPlan(1).subscribe(new Observer<List<WeekDietPlan>>() {
        @Override
        public void onSubscribe(Disposable d) {
            Timber.d(TAG, "onSubscribe() called with: d = [" + d + "]");
            compositeSubscription.add(d);
        }

        @Override
        public void onNext(List<WeekDietPlan> list) {
            Timber.d(TAG, "onNext() called with: value = [" + list.size() + "]");
            swipRefreshLayout_reLoad.setRefreshing(false);

            hideConnectionErrorLayout();

            if (list.size() == 0)
            {
                Toast.makeText(getContext(), R.string.noDietPackageAvailable, Toast.LENGTH_SHORT).show();
            }

            bindRecyclerData(list);


        }

        @Override
        public void onError(Throwable e) {
            e.printStackTrace();
            Timber.d(TAG, "onError() called with: e = [" + e + "]");

            swipRefreshLayout_reLoad.setRefreshing(false);
            if (e instanceof IOException) {
                Toast.makeText(getContext(), R.string.connectionError, Toast.LENGTH_SHORT).show();


            } else if (e instanceof NullPointerException) {

            } else if (e instanceof SessionTimeoutException) {
                AuthenticationManager.logOut();

            } else {
                Toast.makeText(getContext(),
                        e.getMessage(),
                        Toast.LENGTH_SHORT).show();
            }
        }

        @Override
        public void onComplete() {
            Log.d(TAG, "onComplete() called");
        }
    });


}

I know this is late, and directed towards future folks. 我知道这很晚了,并且针对未来的人们。

There is a need to create a Network Interceptor like this 需要创建这样的Network Interceptor

public abstract class NetworkConnectionInterceptor implements Interceptor {

public abstract boolean isInternetAvailable();

public abstract void onInternetUnavailable();

public abstract void onCacheUnavailable();

@Override
public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    if (!isInternetAvailable()) {
        onInternetUnavailable();
        request = request.newBuilder().header("Cache-Control",
                "public, only-if-cached, max-stale=" + 60 * 60 * 24).build();
        Response response = chain.proceed(request);
        if (response.cacheResponse() == null) {
            onCacheUnavailable();
        }
        return response;
    }
    return chain.proceed(request);
}
}

Then add it with your okhttp builder. 然后将其与您的okhttp构建器一起添加。 You can refer to this link . 您可以参考此链接

One more you should take care is to check your response "Cache-control" header . 您还应注意的另一件事是检查response "Cache-control" header Its value has to be like this "max-age=2592000" . 它的值必须像这样"max-age=2592000"

To return the cached data instead of the error, you could use the onErrorReturn operator that: 要返回缓存的数据而不是错误,可以使用onErrorReturn运算符:

Instructs an Observable to emit an item (returned by a specified function) rather than invoking onError if it encounters an error. 指示一个Observable发出一个项(由指定函数返回),而不是在遇到错误时调用onError。

List of the different operators to recover on error: https://github.com/ReactiveX/RxJava/wiki/Error-Handling-Operators 要恢复错误的不同运算符的列表: https : //github.com/ReactiveX/RxJava/wiki/Error-Handling-Operators

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

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