Hello friends, In this tutorial I’ll show you that how to get data from Rest API and store them in cache, so whenever you left current activity and come back to same activity then you don’t need to fetch data again from API. This will reduce API load and speedup your app. The Idea behind this cache memory is that we will get json data from API and save that json data in json file in mobile’s SD Card or cache memory according to our need.

Why we need to do this?

You all use Facebook, Instagram app right? And you noticed that when you open facebook app then data is already loaded which you seen previously and then data get updated automatically, and you don’t need to wait to load data and no blank screen.This is good idea to display old data instead of loading screen or progress bar, which is too annoying. Hope you understand why we need this.

Why I’m not using SQLite DB or any other DB?

We can also do this with SQLite database, but this process is more faster than SQLite, its fater because we get data from API in json format, so will will write the same data in json file. Means we will not parse each and every JsonObject and JsonArray. In case of SQLite we need to parse JsonObjects and JsonArray and all the values exist in API response. And when we display that data into our RecyclerView or any other UI component then we need to get data from SQLIte DB and convert then into model class -> add that model class into ArrayList -> and finally set that ArrayList to adapter.

How json files are after than SQLite DB?

So in json files we will get json response from API and write that json data As it is into json file. No need to parse json data. And when we use that json data we simple get json from json file and process them same as we process data from API and display in RecyclerView.

Complete Example of storing data into cache

In this example I’m using Retrofit2, the same example will be applied to Retrofit1 and any other json parsing technique like Gson, Volley and HttpUrlConnection.

CacheManager.java


import android.content.Context;
import android.os.Build;
import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;

import static com.edablogs.tubelite.BuildConfig.DEBUG;

/**
 * Created by pawneshwer on 11-Dec-16.
 * to save json into cache
 */

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) {
            M.l(TAG,e);
        } catch (IOException e) {
            M.l(TAG,e);
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.flush();
                    outputStream.close();
                } catch (IOException e) {
                    M.l(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;
    }
}

CacheUtils.java (to save name of json file)


/**
 * Created by pawneshwer on 23-Dec-16.
 */

public interface CacheUtils {
    //cache manager file names
    String POPULAR = "popular.json";
    String LATEST = "latest.json";
    String TOP_WATCHED = "top_watched.json";
    String SEARCH = "search.json";
    String RELATED = "related.json";
    String CHANNEL_VIDEOS = "channel_video.json";
}

MainActivity.java


import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.view.View;

import com.edablogs.tubelite.R;
import com.edablogs.tubelite.adapter.RecyclerAdapter;
import com.edablogs.tubelite.api.APIService;
import com.edablogs.tubelite.api.YoutubeApi;
import com.edablogs.tubelite.model.youtube.Item;
import com.edablogs.tubelite.model.youtube.ItemLatest;
import com.edablogs.tubelite.model.youtube.LatestModel;
import com.edablogs.tubelite.model.youtube.YoutubeResponse;
import com.edablogs.tubelite.utils.CacheManager;
import com.edablogs.tubelite.utils.CacheUtils;
import com.edablogs.tubelite.utils.Constants;
import com.edablogs.tubelite.utils.M;
import com.google.gson.reflect.TypeToken;

import java.lang.reflect.Type;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class MainActivity extends AppCompatActivity {

	private static final String TAG = "MainActivity";
	private RecyclerView latestList;
	private RecyclerAdapter latestAdapter;
	private CacheManager cacheManager;
	
	@Override
    	protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            latestList = (RecyclerView) findViewById(R.id.recent_list);
	    cacheManager = new CacheManager(this);
		
	    latestList.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
	    latestAdapter = new RecyclerAdapter(this,new ArrayList<>());
	    latestList.setAdapter(latestAdapter);
		
	    initFromCache(); //load data from cache if exist.
    	}
	

	private void initFromCache() {
        
            Type type = new TypeToken<ArrayList<ItemLatest>>() {}.getType();
            ArrayList<ItemLatest> itemLatest = (ArrayList<ItemLatest>) cacheManager.readJson(type, CacheUtils.LATEST);
            if (itemLatest != null)
                latestAdapter.replaceWith(itemLatest);
    	}
	
	@Override
    	public void onResume() {
            getLatest();
            super.onResume();
    	}
	
	private void getLatest() {
        if (M.isConnectionAvailable(this)) {
            YoutubeApi youtubeApi = APIService.create(YoutubeApi.class);
            Call<LatestModel> latestVideos = youtubeApi.getRecentVideos("video", "date", Constants.KEY, Constants.PART, "10","");
            latestVideos.enqueue(new Callback<LatestModel>() {
                @Override
                public void onResponse(@NonNull Call<LatestModel> call, @NonNull Response<LatestModel> response) {
                    if (response.body() != null) {
						//here is your json data from api, save this into json file.
                        ArrayList<ItemLatest> items = (ArrayList<ItemLatest>) response.body().getItems();
                        if (items != null){
                            latestAdapter.replaceWith(items);
                            Type type = new TypeToken<ArrayList<ItemLatest>>() {
                            }.getType();
                            cacheManager.writeJson(response.body().getItems(), type, CacheUtils.LATEST);
                        }
                    }
                }

                @Override
                public void onFailure(@NonNull Call<LatestModel> call, @NonNull Throwable t) {
                    //api failed to return data due to network problem or something else, display data from json file.
					M.l(TAG, t);
                    Type type = new TypeToken<ArrayList<ItemLatest>>() {
                    }.getType();
                    ArrayList<ItemLatest> itemLatests = (ArrayList<ItemLatest>) cacheManager.readJson(type, CacheUtils.LATEST);
                    if (itemLatests != null)
                        latestAdapter.replaceWith(itemLatests);
                }
            });
        } else {
			//Internet not available, display data from json file.
            Type type = new TypeToken<ArrayList<ItemLatest>>() {
            }.getType();
            ArrayList<ItemLatest> itemLatests = (ArrayList<ItemLatest>) cacheManager.readJson(type, CacheUtils.LATEST);
            if (itemLatests != null)
                latestAdapter.replaceWith(itemLatests);
            Snackbar.make(rootLayout, R.string.no_internet_connection, Snackbar.LENGTH_SHORT).setAction(R.string.retry, new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    MainActivity.this.getLatest();
                }
            }).show();
        }

    }
}

M.java file

public static boolean isConnectionAvailable(Context context) {

        ConnectivityManager connectivityManager = (ConnectivityManager) context
                .getSystemService(Context.CONNECTIVITY_SERVICE);
        if (connectivityManager != null) {
            NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo();
            if (netInfo != null && netInfo.isConnected()
                    && netInfo.isConnectedOrConnecting()
                    && netInfo.isAvailable()) {
                return true;
            }
        }
        return false;
    }

    public static void l(String TAG, String msg) {
        Log.d(TAG, msg);
    }
    public static void l(String TAG, Throwable e) {
        Log.e(TAG, "",e);
    }

ItemLatest.java


package com.edablogs.tubelite.model.youtube;

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class ItemLatest {

    @SerializedName("kind")
    @Expose
    private String kind;
    @SerializedName("etag")
    @Expose
    private String etag;
    @SerializedName("id")
    @Expose
    private Id id;
    @SerializedName("snippet")
    @Expose
    private Snippet snippet;

    /**
     * 
     * @return
     *     The kind
     */
    public String getKind() {
        return kind;
    }

    /**
     * 
     * @param kind
     *     The kind
     */
    public void setKind(String kind) {
        this.kind = kind;
    }

    /**
     * 
     * @return
     *     The etag
     */
    public String getEtag() {
        return etag;
    }

    /**
     * 
     * @param etag
     *     The etag
     */
    public void setEtag(String etag) {
        this.etag = etag;
    }

    /**
     * 
     * @return
     *     The id
     */
    public Id getId() {
        return id;
    }

    /**
     * 
     * @param id
     *     The id
     */
    public void setId(Id id) {
        this.id = id;
    }

    /**
     * 
     * @return
     *     The snippet
     */
    public Snippet getSnippet() {
        return snippet;
    }

    /**
     * 
     * @param snippet
     *     The snippet
     */
    public void setSnippet(Snippet snippet) {
        this.snippet = snippet;
    }

}

APIService.java

package com.edablogs.tubelite.api;

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

import static com.edablogs.tubelite.utils.Constants.BASE_URL;
import static com.edablogs.tubelite.utils.Constants.GIT_BASE_URL;

/**
 * Created by pawneshwer on 11/11/16.
 */

public class APIService {
    public static <S> S create(Class<S> cls){
        Retrofit build = new Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create()).build();
        return build.create(cls);
    }
    public static <S> S createUpdate(Class<S> cls){
        Retrofit build = new Retrofit.Builder().baseUrl(GIT_BASE_URL).addConverterFactory(GsonConverterFactory.create()).build();
        return build.create(cls);
    }
}

YoutubeApi.java

package com.edablogs.tubelite.api;

import com.edablogs.tubelite.model.update.UpdateModel;
import com.edablogs.tubelite.model.youtube.ChannelModel;
import com.edablogs.tubelite.model.youtube.LatestModel;
import com.edablogs.tubelite.model.youtube.YoutubeResponse;

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;

/**
 * Created by pawneshwer on 11/11/16.
 */

public interface YoutubeApi {
    
    @GET("search")
    Call<LatestModel> getRecentVideos(@Query("type") String type,
                                      @Query("order") String order,
                                      @Query("key") String key,
                                      @Query("part") String part,
                                      @Query("maxResults") String maxResults,
                                      @Query("pageToken") String lastToken);

}