【问题标题】:Android Volley Returning results twice for one requestAndroid Volley 一个请求返回两次结果
【发布时间】:2013-11-17 06:43:03
【问题描述】:

我已经尝试解决这个问题两天了,我完全被难住了。出于某种原因,我向队列发送了一个请求,但 volley 将它返回了两次,即调用了两次侦听器并将我的列表视图中的结果加倍。我打开了 Volley 的日志记录,我可以看到请求被添加到队列并返回,然后几秒钟后,同样的请求也被返回。在下方登录

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

如您所见,它从来没有说添加了另一个请求,也没有说超过一个请求在进行中。如果我清除缓存,我会得到相同的结果,只是第一个请求来自网络,第二个请求是从缓存返回的。我已经尝试调试并单步执行我的代码,但我从来没有看到请求被排队超过一次。有没有人见过这个?还有什么我应该看的吗?

谢谢

编辑:这是我启动 volley 并调用它的代码。

@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;
    }

}

【问题讨论】:

  • 我们可以看看一些代码吗! :D
  • 确定有你需要看的特定部分吗?我将添加创建队列的位置和添加请求的代码。
  • 您是否有自定义重试策略?
  • 不,我唯一接触过的 volley 部分是缓存,我添加了一个方法来忽略服务器返回的缓存头并将所有内容缓存 24 小时。但是即使我使用默认的 CacheHeaderParser 方法也会发生这种情况。

标签: android android-volley


【解决方案1】:

我相信在更多地浏览代码之后我已经弄明白了。我相信这是导致这种行为的softttl。在 Volley 缓存调度程序中

            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.
                        }
                    }
                });

这会发布响应,然后将其发送到网络,网络再向同一个侦听器发布另一个响应。

【讨论】:

  • 在看到这篇文章之前发现了这个。正在寻找一种忽略缓存并使用新数据的方法,除非下载失败(没有互联网等)
  • @Losin'Me 嗨,您找到解决方案了吗?我面临同样的问题。您可以在这里添加答案吗?
  • 我们有一个下载方法,它接受一个布尔值“useCache”,如果为真,它将检查给定 url 的缓存。如果缓存条目存在,则检查它是否已过期,如果没有,则使用它,否则我们使用setShouldCache(false) 发出新请求。唯一的事情是我们必须自己做缓存,这意味着我们自己的请求项扩展了请求,但保留它的缓存条目并将它传递给一个侦听器,这样我们就可以将它放入缓存中时请求成功。
【解决方案2】:

好的,我遇到了这个问题并想出了一个解决方案。在我的自定义类中,我在顶部创建了一个全局变量,您可以在下面的代码中看到

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

然后在构造函数中初始化它

    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);
}

最后我重写了deliverResponse 方法。第一次 mDelivered 将是假的,一旦交付它就会是真的。如果响应来自缓存,则取消网络请求

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

希望对你有帮助

【讨论】:

    【解决方案3】:

    要停止多次请求,您可以使用请求对象的 setRetryPolicy() 方法为您的请求对象配置重试策略。我设法用以下代码做到了这一点:

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

    使用上面的代码,我将超时时间减少到 2 秒,并将重试次数设置为 0。

    【讨论】:

    • 这个答案从来没有用过,看到人们甚至没有尝试就复制粘贴这个答案真的很痛苦。
    猜你喜欢
    • 2023-04-05
    • 1970-01-01
    • 1970-01-01
    • 2020-04-25
    • 1970-01-01
    • 1970-01-01
    • 2022-01-10
    • 2020-12-04
    • 1970-01-01
    相关资源
    最近更新 更多