Volley网络框架发送请求后,页面退出时需要取消已经添加但还没有完成的请求,否者会导致内存泄漏或者crash,比如请求中持有上下文,Activity,但是网络请求还没有完成,仍然持有引用,导致该回收的内存,gc无法回收,从而导致内存泄漏的发生。还有如果Activity退出时没有取消请求,在请求回调设置页面数据时,此时页面已经销毁,但是仍然给页面的控件设置数据时,就会导致空指针的发生。
Volley的使用方式
请求队列RequestQueue为单例,避免重复创建。
public class MyVolley {
private static RequestQueue mRequestQueue;
public static RequestQueue getRequestQueue() {
if (mRequestQueue != null) {
return mRequestQueue;
} else {
throw new IllegalStateException("RequestQueue not initialized");
}
}
public static void addRequest(BaseRequest<ResponseData> request, String tag) {
request.setTag(tag);
getRequestQueue().add(request);
getRequestQueue().start();
}
}
每个Activity有一个TAG,请求时使用这个TAG,onDestroy时通过TAG取消。
public class SampleActivity extends Activity {
private static final String TAG = "SampleActivity";
//...
MyVolley.addRequest(request, TAG);
public void onDestroy() {
MyVolley.cancelAll(TAG);
super.onDestroy();
}
}
出现的问题
重复进入一个Activity时,有时会出现第二次进入时,网络请求始终不能加载出来。
原因:当一个页面退出时,onStop会立即调用,但安卓并不保证onDestroy也会立即调用。onDestroy的调用可能有延迟,当第二个SampleActivity请求刚发起还没返回结果,刚好前一个SampleActivity的onDestroy调用了,因为两者的TAG相同,于是第二个SampleActivity的网络请求被取消掉了,永远不会加载出结果。
解决方法
- 不使用单例请求队列。这种做法效率低下,不考虑。
- onStop中调用cancel。官方资料是这么用的,但是不一定能满足需求:http://developer.android.com/intl/zh-cn/training/volley/simple.html
- 每次创建Activity,生成一个新的TAG。例如可以直接在基类中用Activity的ClassName和hashCode或时间戳生成。这种做法相对比较好。
- 直接将Activity实例自身作为TAG传入,每次创建新的Activity实例,TAG就变了。这种做法有点担心内存泄露,不是很推荐。
参考: http://www.pocketdigi.com/20140511/1315.html
volley多次重复请求的问题
Volley默认请求多次原因解析
由于Volley默认设置的超时时间比较短,只有2.5秒,这在天朝这种复杂的网络环境下简直是不可想象的,请求发送的数据量稍微大一点,或者2G、移动3G这种龟速网下,或者服务器处理速度慢一点,很容易发生TimeOut。当请求发出后,2.5秒没收到服务器的响应(有可能服务器已经收到了请求,在做逻辑处理,还没返回结果),则客户端抛出TimeOut异常,失败一次后,进入DefaultRetryPolicy的retry(),判断当前请求的次数(1)<=默认的最大请求次数(1),所以不抛出异常,回到BasicNetwork的performRequest()方法继续执行while(true)循环,即再发一次request请求。
解决方法
增加默认的超时时间或者设置本次提交数据的请求不再使用重试策略。
request.setRetryPolicy(new DefaultRetryPolicy(20*1000, 0,1f));
参数含义:
- 设置volley请求超时,避免重复请求
- 20*1000代表超时时间,0代表重复请求次数
- 后面的1f也可以0f,代表递增,我猜是volley请求接口
- 第二次比第一次,它的时间间隔会自己增大,依次增大