Volley - How to Cache loaded data to disk and load them when no connection available or at start of the application

StackOverflow https://stackoverflow.com/questions/21902063

  •  14-10-2022
  •  | 
  •  

Question

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 ?

/**
 * 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<PicasaEntry> mEntries = new ArrayList<PicasaEntry>();
    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 && !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&max-results="
                        +
                        RESULTS_PAGE_SIZE
                        +
                        "&thumbsize=160&alt=json"
                        + "&start-index="
                        + startIndex,
                        null,
                        createMyReqSuccessListener(),
                        createMyReqErrorListener());


        if (myReq.getCacheEntry().data != null) {

        }
        queue.add(myReq);
    }


    private Response.Listener<JSONObject> createMyReqSuccessListener() {
        return new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                try {
                    JSONObject feed = response.getJSONObject("feed");
                    JSONArray entries = feed.getJSONArray("entry");
                    JSONObject entry;
                    for (int i = 0; i < entries.length(); i++) {
                        entry = entries.getJSONObject(i);

                        String url = null;

                        JSONObject media = entry.getJSONObject("media$group");
                        if (media != null && media.has("media$thumbnail")) {
                            JSONArray thumbs = media.getJSONArray("media$thumbnail");
                            if (thumbs != null && thumbs.length() > 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 > previousTotal) {
                    loading = false;
                    previousTotal = totalItemCount;
                    currentPage++;
                }
            }
            if (!loading && (totalItemCount - visibleItemCount) <= (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;
        }
    }

The Adapter:

public class PicasaArrayAdapter extends ArrayAdapter<PicasaEntry> {
    private ImageLoader mImageLoader;

    public PicasaArrayAdapter(Context context, 
                              int textViewResourceId, 
                              List<PicasaEntry> 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);
        }
    }
}
Was it helpful?

Solution

This is pretty easy with volley as caching is build in. All you need is to call getCache with url of the request. Of course keep in mind that server is responsible to set max age of the cache and if time was passed cache is cleared

For example try this:

if(queue.getCache().get(url)!=null){
  //response exists
  String cachedResponse = new String(queue.getCache().get(url).data);
}else{
  //no response
  queue.add(stringRequest);
}

For caching is great to use VolleyExtended: eu.the4thfloor.volleyextended There is updated Response listener, where you get additional boolean changed which means if response is different to last response in cache. And on error response there is additional parameter response (cached) which you can use in case of server or network outtage. Of course if it is cached only.

private class ResponseListenerYYY extends Api.ResponseListener {
@Override
public void onResponse(String response, boolean changed) {

}

@Override
public void onErrorResponse(VolleyError error, String response) {


}

}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top