android-cn / android-discuss

Android 问题交流讨论坛, 微信公众号:codekk, 网站:
https://github.com/android-cn/android-discuss/issues
Apache License 2.0
4.08k stars 536 forks source link

[问答] 防止重复发送网络请求 #195

Open leiweibo opened 9 years ago

leiweibo commented 9 years ago

当点击activity上的一个按钮,发送网络请求,在网络比较慢的情况下,用户可能会继续去点击按钮,这个时候,发送其他无谓的请求,不知道大家是怎么处理这类问题来拦截?

目前的想法是,用户点击了按钮发送网络请求之后,设置一个isProcessing的变量为true,当网络回调返回的时候,isProcessing = false; 点击按钮之后,如果isProcessing == true, 则直接return;

这种方法应该是没有问题的,但是涉及到改动的地方比较大,不知道有没有更加智能的方法,来个全局控制。

Caij commented 9 years ago

在网络层直接写一个Map存储请求,完成或者失败删除请求, 再次请求时判断map中是否存在, 不存在才去请求网络。

leiweibo commented 9 years ago

@Caij 挺好的一个想法, 下午我在看AOP,隐隐约约AOP也可以优雅地把这个问题给处理了,对AOP了解还不深,暂时没有实质性的方案。

ronanhardiman commented 9 years ago

volley 好像可以过滤相同的请求.

junyuecao commented 9 years ago

Volley中如果开了缓存的话, 相同的请求同时只会有一个去真正的请求, 后续都走缓存, 虽然不会请求多次, 但是回调是会执行多次的, 和这个需求不match

ronanhardiman commented 9 years ago

@junyuecao 是有一些问题,修改volley 应该可以match 这个需求

leiweibo commented 9 years ago

@ronanhardiman volley怎么改可以提供一下思路吗?

miao1007 commented 9 years ago
  1. HTTP header中加入max-age,这样某个固定的时间内都将返回empty body,当然这个方法是死的,把时间完全限制了,这个方法回掉也会同样要执行多次。
  2. 还有个晕招,就是直接设置按钮的clickable为false,或者使用progressbar,类似于楼主的方法,比如点赞的场景。
  3. 使用Map的话,在回掉的时候,还是需要回收HashMap的,维护Map还不如只维护一个boolean呢。
dxjia commented 9 years ago

对于这种场景,我的处理方式是延迟显示progress:就是在发起请求时并不立即显示progress,而是延迟一段时间,如果请求还没回来再显示,而如果请求在规定的时间返回啦,就不显示progress,这样对于一般网络通畅的情况下没有影响,而网络差的情况下可以通过pregressbar的显示屏蔽掉用户的多余请求操作,另一方面也显式提醒了用户当前网络不好,可以通过handler+runnable实现:

    //请求开始时
    final Handler handler = new Handler();
    final ProgressDialog progressDialog = new ProgressDialog(this);
    progressDialog.setTitle("*****");
    progressDialog.setMessage("*****");
    progressDialog.setCancelable(false);

    final Runnable showProgress = new Runnable() {
        @Override
        public void run() {
            progressDialog.show();
        }
    };
    // 设置1S后显示ProgressDialog
    handler.postDelayed(showProgress, 1000);

     ...............

    // 请求返回或超时时
    handler.removeCallbacks(showProgress);
    progressDialog.dismiss();

这种方式需要设定一个合理的延迟时间,时间太久用户还是可能会点多次。。。 所以这种方式一定程度上是在规避或者弱化这个问题,并没有根本解决;

AnyLifeZLB commented 5 years ago

我们以前的做法是网络请求层全局拦截并map存储请求,http 请求来了如果map 中不存在【相同的请求】就可以继续请求,请求返回不管成功失败删除Map 中的记录;否则拦截停止🤚。不过换成Retrofit 封装后有点麻烦不好拦截并【停止http请求】,但是我们可以返回一个自定义的特定的错误okhttp3.Response 并统一的全局hack 这个错误就好了

https://www.jianshu.com/p/e0e4eba73bbf

hss01248 commented 4 years ago

新鲜出炉的解决方案: https://www.jianshu.com/p/39ea6c46cc22

JeckChou commented 4 years ago

retrofit + rxjava, 使用rx的防抖,或是使用rxbinding,按钮防抖