0

This class contains the APIClient instance for calling API but here there is one problem while fetching cache. I want to fetch data from cache memory while device is not connected to network.

private static Retrofit retrofit = null;
private static final String CACHE_CONTROL = "Cache-Control";

public static Retrofit getClient(Context context)
{
    if (retrofit==null) {
        retrofit = new Retrofit.Builder()
                .baseUrl(URLS.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .client(provideOkHttpClient(context))
                .build();
    }
    return retrofit;
}

/**
 * Add Client for adding Authentication headers.
 * @return Retrofit
 */
public static Retrofit getAthenticationClient()
{
    if (retrofit==null) {
        retrofit = new Retrofit.Builder()
                .baseUrl(URLS.BASE_URL)
                .client(ApiIntercepters.AddAuthenticationHeader())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }
    return retrofit;
}

public static OkHttpClient provideOkHttpClient(Context context)
{
    return new OkHttpClient.Builder()
            .addNetworkInterceptor(provideCacheInterceptor())
            .cache( provideCache(context))
            .build();
}

private static Cache provideCache (Context context)
{
    Cache cache = null;
    try
    {
        //setup cache
        File httpCacheDirectory = new File(context.getCacheDir(), "responses");
        int cacheSize = 10 * 1024 * 1024; // 10 MiB
        cache = new Cache(httpCacheDirectory, cacheSize);

    }
    catch (Exception e)
    {
        Log.e( "Injector :-> ", "Could not create Cache!" );
    }
    return cache;
}

public static Interceptor provideCacheInterceptor ()
{
    return new Interceptor()
    {
        @Override
        public Response intercept (Chain chain) throws IOException
        {
            Response originalResponse = chain.proceed(chain.request());
            if (RetrofitDemoApp.hasNetwork()) {
                int maxAge = 60; // read from cache for 1 minute
                return originalResponse.newBuilder()
                        .header("Cache-Control", "public, max-age=" + maxAge)
                        .build();
            } else {
                int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale
                return originalResponse.newBuilder()
                        .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                        .build();
            }
        }
    };
}
James Z
  • 12,209
  • 10
  • 24
  • 44
Akash Patel
  • 218
  • 2
  • 10
  • Why don't you just save the data when you retrieve it for later use? – Strahinja Ajvaz Dec 24 '16 at 05:23
  • Can you please tell me how i can save data with cache memory in retrofit? I am new with Retrofit caching management. – Akash Patel Dec 24 '16 at 05:52
  • Have a look here http://stackoverflow.com/questions/23429046/can-retrofit-with-okhttp-use-cache-data-when-offline – Strahinja Ajvaz Dec 24 '16 at 05:54
  • Thanks!!! I am getting solution but while getting response i am getting 504 error into offline mode: @Override public void onResponse(Call call, Response response) { parameters.DismissLoader(); try { if (response.code() == 200) { GetRegisterResponse(response.body().string()); } } catch (IOException e) { e.printStackTrace(); } – Akash Patel Dec 24 '16 at 06:17
  • It means that the gateway timed out. My guess would be that you made a mistake in your URL, missing a symbol or space or something trivial. When you debug the onResponse method have a look at the response object and copy the URL. Test said URL in a web browser and see if it's working properly. i.e. you get the response you are after. – Strahinja Ajvaz Dec 24 '16 at 06:32
  • Actually i am getting response properly while internet connected but it will not provide proper response while device is not connected to internet. At that time may be it will return response from cache if i am wrong please correct me. – Akash Patel Dec 24 '16 at 06:36
  • Not really sure what you mean, but how would you get a response from anything if you're not online? Isn't your objective to cache the response so that if the device isn't online, you have that "copy" to go by? – Strahinja Ajvaz Dec 24 '16 at 06:41
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/131394/discussion-between-akash-patel-and-stanna). – Akash Patel Dec 24 '16 at 06:41

1 Answers1

0

Here is a solution I also want the same and I implement it and its works properly for storing data in the cache and then fetching the data.

Check this below code

CacheManager.java

 public class CacheManager {

    private Context context;
    private static final String TAG = CacheManager.class.getSimpleName();

    public CacheManager(Context context) {
        this.context = context;
    }

    public void writeJson(Object object, Type type, String fileName) {
        File file = new File(context.getCacheDir(), fileName);
        OutputStream outputStream = null;
        Gson gson = new GsonBuilder().enableComplexMapKeySerialization().setPrettyPrinting().create();
        try {
            outputStream = new FileOutputStream(file);
            BufferedWriter bufferedWriter;
            if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream,
                        StandardCharsets.UTF_8));
            } else {
                bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream, "UTF-8"));
            }

            gson.toJson(object, type, bufferedWriter);
            bufferedWriter.close();

        } catch (FileNotFoundException e) {
            Log.i(TAG,""+e);
        } catch (IOException e) {
            Log.i(TAG,""+e);
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.flush();
                    outputStream.close();
                } catch (IOException e) {
                    Log.i(TAG,""+e);
                }
            }
        }

    }


    public Object readJson(Type type, String fileName) {
        Object jsonData = null;

        File file = new File(context.getCacheDir(), fileName);
        InputStream inputStream = null;
        Gson gson = new GsonBuilder().enableComplexMapKeySerialization().setPrettyPrinting().create();
        try {
            inputStream = new FileInputStream(file);
            InputStreamReader streamReader;
            if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                streamReader = new InputStreamReader(inputStream,
                        StandardCharsets.UTF_8);
            } else {
                streamReader = new InputStreamReader(inputStream, "UTF-8");
            }

            jsonData = gson.fromJson(streamReader, type);
            streamReader.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
            if (DEBUG) Log.e(TAG, "loadJson, FileNotFoundException e: '" + e + "'");
        } catch (IOException e) {
            e.printStackTrace();
            if (DEBUG) Log.e(TAG, "loadJson, IOException e: '" + e + "'");
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    if (DEBUG) Log.e(TAG, "loadJson, finally, e: '" + e + "'");
                }
            }
        }
        return jsonData;
    }

}

For store and fetch data in the cache

MainActivity.java


if (checkInternetConnection(getContext())) {
            progressBar.setVisibility(View.VISIBLE);
            Api mApiService = RetrofitClient.getClient(Api.BASE_URL).create(Api.class);

            Call<ApiModel> call = mApiService.getCountry();

            call.enqueue(new Callback<ApiModel>() {
                @Override
                public void onResponse(Call<ApiModel> call, Response<ApiModel> response) {
                    countryList = response.body();

                    countryListData = countryList.data;


                    CacheManager cacheManager = new CacheManager(MainActivity.this);

                    //store data in cache
                    Type type = new TypeToken<ApiModel>() {
                    }.getType();
                    cacheManager.writeJson(response.body(), type, "latest.json");

                    adapter = new MyListAdapter(getApplicationContext(), countryListData);
                    LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getApplicationContext());
                    recyclerView.setHasFixedSize(true);
                    recyclerView.setLayoutManager(linearLayoutManager);
                    recyclerView.setAdapter(adapter);

                    progressBar.setVisibility(View.GONE);
                    Log.i(TAG, "onResponse: SuccessFull");

                }

                @Override
                public void onFailure(Call<ApiModel> call, Throwable t) {
                    Toast.makeText(getApplicationContext(), "An error has occurred", Toast.LENGTH_LONG).show();

                    //api failed to return data due to network problem or something else, display data from cache file
                    Type type = new TypeToken<ApiModel>() {
                    }.getType();
                    countryList = (ApiModel) cacheManager.readJson(type, "latest.json");


                    Log.i(TAG, "onFailure: " + t);
                    progressBar.setVisibility(View.GONE);
                }

            });
        } else {

            //No internet connected then fetch data if exists
            Type type = new TypeToken<ApiModel>() {
            }.getType();
            countryList = (ApiModel) cacheManager.readJson(type, "latest.json");
            System.out.println("cacheData" + countryList.data.get(1));
            if (countryList != null) {
                countryListData = countryList.data;
                adapter = new MyListAdapter(getApplicationContext(), countryListData);
                LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getApplicationContext());
                recyclerView.setHasFixedSize(true);
                recyclerView.setLayoutManager(linearLayoutManager);
                recyclerView.setAdapter(adapter);
            }
        }