Android Volley Returning results twice for one request
Asked Answered
Y

3

10

I have been trying to figure out this issue for two days now and I am completly stumped. For some reason I am sending one request to the queue but volley is returning it twice, which is calling the listener twice and doubling the results in my listview. I turned on the logging for Volley and I can see the request being added to the queue and returned, then a few seconds later the same request is also returned. Log below

V/Volley(14666): [188] CacheDispatcher.run: start new dispatcher
11-15 12:29:30.152: V/Volley(14666): [1] RequestQueue.add: Request for cacheKey=http://reallylongurl is in flight, putting on hold.
11-15 12:29:39.722: V/Volley(14666): [1] RequestQueue.finish: Releasing 1 waiting requests for cacheKey=http://reallylongurl.
11-15 12:29:39.722: D/Volley(14666): [1] MarkerLog.finish: (9809 ms) [ ] http://reallylongurl 0xd68d6603 NORMAL 1
11-15 12:29:39.732: D/Volley(14666): [1] MarkerLog.finish: (+0   ) [ 1] add-to-queue
11-15 12:29:39.732: D/Volley(14666): [1] MarkerLog.finish: (+2169) [188] cache-queue-take
11-15 12:29:39.742: D/Volley(14666): [1] MarkerLog.finish: (+37  ) [188] cache-hit
11-15 12:29:39.742: D/Volley(14666): [1] MarkerLog.finish: (+6878) [188] cache-hit-parsed
11-15 12:29:39.742: D/Volley(14666): [1] MarkerLog.finish: (+0   ) [188] post-response
11-15 12:29:39.752: D/Volley(14666): [1] MarkerLog.finish: (+725 ) [ 1] done

A few other requests get queued here.

11-15 12:29:48.405: D/Volley(14666): [1] MarkerLog.finish: (18302 ms) [ ] http://reallylongurl 0xd68d6603 NORMAL 2
11-15 12:29:48.412: D/Volley(14666): [1] MarkerLog.finish: (+0   ) [ 1] add-to-queue
11-15 12:29:48.412: D/Volley(14666): [1] MarkerLog.finish: (+15164) [188] cache-queue-take
11-15 12:29:48.412: D/Volley(14666): [1] MarkerLog.finish: (+220 ) [188] cache-hit
11-15 12:29:48.432: D/Volley(14666): [1] MarkerLog.finish: (+2299) [188] cache-hit-parsed
11-15 12:29:48.432: D/Volley(14666): [1] MarkerLog.finish: (+0   ) [188] post-response
11-15 12:29:48.442: D/Volley(14666): [1] MarkerLog.finish: (+619 ) [ 1] done

As you can see it never says another request was added nor does it say more then one request was in flight. If I clear out the cache I get the same result, just the first request is from network and the second is returned from cache. I have tried debugging and stepping through my code but I never see the request being enqueued more then once. Has anyone seen this before? Any where else I should look?

Thanks

Edit: Here is the code where I start up volley and where I call it.

@Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        if (savedInstanceState == null) {

            TextView emptyView = new TextView(getActivity());
            emptyView.setLayoutParams(new LayoutParams(
                    LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
            emptyView.setText("Loading....");
            emptyView.setTextSize(20);
            emptyView.setGravity(Gravity.CENTER_VERTICAL
                    | Gravity.CENTER_HORIZONTAL);

            ((ViewGroup) mListView.getParent()).addView(emptyView);
            mListView.setEmptyView(emptyView);
        }

        mAdapter = new OttoArrayAdapter(mContext);
        mListView.setOnScrollListener(onScrollListener);
        mListView.setAdapter(mAdapter);

        String url = Util.getURL("", mContext);

        HttpRequest request = new HttpRequest(url);
        request.setTag(mContext);

        VolleyLoader.getInstance(mContext).getRequestQueue().add(request);

    }



public class VolleyLoader {
    private static VolleyLoader mInstance = null;
    private RequestQueue mRequestQueue;
    private ImageLoader mImageLoader;

    private VolleyLoader(Context context) {

        OkHttpStack stack = new OkHttpStack();
        mRequestQueue = Volley.newRequestQueue(context, stack);
        mImageLoader = new ImageLoader(this.mRequestQueue, new LruBitmapCache(
                Util.getCacheSize(context)));
    }

    public static VolleyLoader getInstance(Context context) {
        if (mInstance == null) {
            mInstance = new VolleyLoader(context);
        }
        return mInstance;
    }

    public RequestQueue getRequestQueue() {
        return this.mRequestQueue;
    }

    public ImageLoader getImageLoader() {
        return this.mImageLoader;
    }

}
Yoghurt answered 17/11, 2013 at 6:43 Comment(4)
Can we see some code please! :DDollhouse
Sure is there a specific part you need to see? I'll add where I create the queue and the code that adds the request.Yoghurt
do you have like a custom retry policy?Dollhouse
no, the only part of volley I have touched is caching, I added a method to ignore the cache-headers returned by the server and caching everything for 24 hours. But this happens even if I use the default CacheHeaderParser method.Yoghurt
Y
6

I believe I have figured it out after stepping through the code more. I believe it is the softttl that is causing this behavior. In the Volley cachedispatcher

            if (!entry.refreshNeeded()) {
                // Completely unexpired cache hit. Just deliver the response.
                mDelivery.postResponse(request, response);
            } else {
                // Soft-expired cache hit. We can deliver the cached response,
                // but we need to also send the request to the network for
                // refreshing.
                request.addMarker("cache-hit-refresh-needed");
                request.setCacheEntry(entry);

                // Mark the response as intermediate.
                response.intermediate = true;

                // Post the intermediate response back to the user and have
                // the delivery then forward the request along to the network.
                mDelivery.postResponse(request, response, new Runnable() {
                    @Override
                    public void run() {
                        try {
                            mNetworkQueue.put(request);
                        } catch (InterruptedException e) {
                            // Not much we can do about this.
                        }
                    }
                });

This posts the response then sends it to the network which posts another response to the same listener.

Yoghurt answered 18/11, 2013 at 5:1 Comment(3)
Found this out just before seeing this post. Was looking for a way to ignore the cache and use the fresh data, unless of course the download failed (no internet etc)Periodontal
@Losin'Me Hi, Did you found any solution? I facing the same issue. Can you add a answer here?Gregale
We have a download method that takes a bool "useCache" that if true will check the cache for the given url. If a cache entry exists then we check if it has expired, if not we use it, otherwise we make a new request with setShouldCache(false). The only thing is that then we have to do the caching ourselves, which meant making our own Request item that extends Request, but holds on to it's Cache entry and passes it through a listener, so that we can put it into the cache when the request was successful.Periodontal
T
2

Okay I faced the issue and came up with a solution. In my custom class i created a global variable on top as you can see in below code

MyRequest extends StringRequest {
    private boolean mDelivered;  //boolean to deliver response only once

then in constructor i initialsed it

    public MyRequest(String url, Listener<String> listener,
                      ErrorListener errorListener, Context ctx) {
    super(url, listener, errorListener);
    mContext = ctx.getApplicationContext();
    mDelivered = false;
    DefaultRetryPolicy retryPolicy = new DefaultRetryPolicy(5000, 1, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
    setRetryPolicy(retryPolicy);
}

and finally i overrided deliverResponse method. For first time mDelivered will be false and once it's delivered it'll be true. if response delivered from cache then cancel network request

    @Override
protected void deliverResponse(String response) {
    if (!mDelivered) {
        mDelivered  = true;
        cancel();   //if cache delivers response better to cancel scheduled network request
        super.deliverResponse(response);
    }
}

Hope this helps

Turro answered 9/5, 2018 at 11:57 Comment(0)
L
-1

To stop the multiple request you can configure retry policy for your request object by using the setRetryPolicy() method of the request object. I managed to do this with following code:

req.setRetryPolicy(new DefaultRetryPolicy(20 * 1000, 0,
      DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));

With the above code, i reduced the timeout to 2 seconds and set the number of retries to 0.

Lomond answered 24/8, 2015 at 10:33 Comment(1)
this answer did never work and its really painful watching people copy pasting this answer without even trying.Redneck

© 2022 - 2024 — McMap. All rights reserved.