title: Volley - How to Cache loaded data to disk and load them when no connection available or at start of the application
body: <p>I want to use Volley to build my rest client. I found some good example (posted below) that do exactly what I want except the caching. I want to  cache the results to the sdcard and load the cached result on starting the app. Any clue for how to modify the code below to enable caching for text and images of the list view items ?</p>
  
  <pre><code>/**
   * Demonstrates: 1. ListView which is populated by HTTP paginated requests; 2. Usage of NetworkImageView; 
   * 3. "Endless" ListView pagination with read-ahead
   * 
   * Please note that for production environment you will need to add functionality like handling rotation, 
   * showing/hiding (indeterminate) progress indicator while loading, indicating that there are no more records, etc...
   *   
   * @author Ognyan Bankov (ognyan.bankov@bulpros.com)
   *
   */
  public class Act_NetworkListView extends Activity {
      private static final int RESULTS_PAGE_SIZE = 20;
  
      private ListView mLvPicasa;
      private boolean mHasData = false;
      private boolean mInError = false;
      private ArrayList&lt;PicasaEntry&gt; mEntries = new ArrayList&lt;PicasaEntry&gt;();
      private PicasaArrayAdapter mAdapter;
  
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.act__network_list_view);
  
          mLvPicasa = (ListView) findViewById(R.id.lv_picasa);
  
          mAdapter = new PicasaArrayAdapter(this, 0, mEntries, new ImageLoader(queue, new BitmapCache(4)));
  
          mLvPicasa.setAdapter(mAdapter);
          mLvPicasa.setOnScrollListener(new EndlessScrollListener());
      }
  
  
      @Override
      protected void onResume() {
          super.onResume();
  
          if (!mHasData &amp;&amp; !mInError) {
              loadPage();
          }
      }
  
      RequestQueue queue;
      private void loadPage() {
          queue = MyVolley.getRequestQueue();
  
          int startIndex = 1 + mEntries.size();
          JsonObjectRequest myReq = new JsonObjectRequest(Method.GET,
                  "https://picasaweb.google.com/data/feed/api/all?q=kitten&amp;max-results="
                          +
                          RESULTS_PAGE_SIZE
                          +
                          "&amp;thumbsize=160&amp;alt=json"
                          + "&amp;start-index="
                          + startIndex,
                          null,
                          createMyReqSuccessListener(),
                          createMyReqErrorListener());
  
  
          if (myReq.getCacheEntry().data != null) {
  
          }
          queue.add(myReq);
      }
  
  
      private Response.Listener&lt;JSONObject&gt; createMyReqSuccessListener() {
          return new Response.Listener&lt;JSONObject&gt;() {
              @Override
              public void onResponse(JSONObject response) {
                  try {
                      JSONObject feed = response.getJSONObject("feed");
                      JSONArray entries = feed.getJSONArray("entry");
                      JSONObject entry;
                      for (int i = 0; i &lt; entries.length(); i++) {
                          entry = entries.getJSONObject(i);
  
                          String url = null;
  
                          JSONObject media = entry.getJSONObject("media$group");
                          if (media != null &amp;&amp; media.has("media$thumbnail")) {
                              JSONArray thumbs = media.getJSONArray("media$thumbnail");
                              if (thumbs != null &amp;&amp; thumbs.length() &gt; 0) {
                                  url = thumbs.getJSONObject(0).getString("url");
                              }
                          }
  
                          mEntries.add(new PicasaEntry(entry.getJSONObject("title").getString("$t"), url));
                      }
                      mAdapter.notifyDataSetChanged();
                  } catch (JSONException e) {
                      showErrorDialog();
                  }
              }
          };
      }
  
  
      private Response.ErrorListener createMyReqErrorListener() {
          return new Response.ErrorListener() {
              @Override
              public void onErrorResponse(VolleyError error) {
                  showErrorDialog();
              }
          };
      }
  
  
      private void showErrorDialog() {
          mInError = true;
  
          AlertDialog.Builder b = new AlertDialog.Builder(Act_NetworkListView.this);
          b.setMessage("Error occured");
          b.show();
      }
  
  
      /**
       * Detects when user is close to the end of the current page and starts loading the next page
       * so the user will not have to wait (that much) for the next entries.
       * 
       * @author Ognyan Bankov (ognyan.bankov@bulpros.com)
       */
      public class EndlessScrollListener implements OnScrollListener {
          // how many entries earlier to start loading next page
          private int visibleThreshold = 5;
          private int currentPage = 0;
          private int previousTotal = 0;
          private boolean loading = true;
  
          public EndlessScrollListener() {
          }
          public EndlessScrollListener(int visibleThreshold) {
              this.visibleThreshold = visibleThreshold;
          }
  
          @Override
          public void onScroll(AbsListView view, int firstVisibleItem,
                  int visibleItemCount, int totalItemCount) {
              if (loading) {
                  if (totalItemCount &gt; previousTotal) {
                      loading = false;
                      previousTotal = totalItemCount;
                      currentPage++;
                  }
              }
              if (!loading &amp;&amp; (totalItemCount - visibleItemCount) &lt;= (firstVisibleItem + visibleThreshold)) {
                  // I load the next page of gigs using a background task,
                  // but you can call any function here.
                  loadPage();
                  loading = true;
              }
          }
  
          @Override
          public void onScrollStateChanged(AbsListView view, int scrollState) {
  
          }
  
  
          public int getCurrentPage() {
              return currentPage;
          }
      }
  </code></pre>
  
  <p>The Adapter:</p>
  
  <pre><code>public class PicasaArrayAdapter extends ArrayAdapter&lt;PicasaEntry&gt; {
      private ImageLoader mImageLoader;
  
      public PicasaArrayAdapter(Context context, 
                                int textViewResourceId, 
                                List&lt;PicasaEntry&gt; objects,
                                ImageLoader imageLoader
                                ) {
          super(context, textViewResourceId, objects);
          mImageLoader = imageLoader;
      }
  
  
      @Override
      public View getView(int position, View convertView, ViewGroup parent) {
          View v = convertView;
  
          if (v == null) {
              LayoutInflater vi = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
              v = vi.inflate(R.layout.lv_picasa_row, null);
          }
  
          ViewHolder holder = (ViewHolder) v.getTag(R.id.id_holder);       
  
          if (holder == null) {
              holder = new ViewHolder(v);
              v.setTag(R.id.id_holder, holder);
          }        
  
          PicasaEntry entry = getItem(position);
          if (entry.getThumbnailUrl() != null) {
              holder.image.setImageUrl(entry.getThumbnailUrl(), mImageLoader);
          } else {
              holder.image.setImageResource(R.drawable.no_image);
          }
  
          holder.title.setText(entry.getTitle());
  
          return v;
      }
  
  
      private class ViewHolder {
          NetworkImageView image;
          TextView title; 
  
          public ViewHolder(View v) {
              image = (NetworkImageView) v.findViewById(R.id.iv_thumb);
              title = (TextView) v.findViewById(R.id.tv_title);
  
              v.setTag(this);
          }
      }
  }
  </code></pre>