那些年我使用Volley遇到的坑
使用Volery已经快整整一年了,下面我来总结一下,我使用Volley时踩到的坑
(一) Volley的二次封装
下面看看我是怎么对Volley的二次封装的:
protected <T> void doSimpleRequest(String url, HashMap<String, String> params, final Class<?> clazz, final SimpleCallback callback) { String requestUrl = urlBuilder(url, params); Logger.e("url", requestUrl); Response.Listener<String> successListener = new Response.Listener<String>() { @Override public void onResponse(String response) { try { T bean = GsonUtils.getFromStr(clazz, response); callback.onResponse(bean); } catch (Exception e) { callback.onError(); } } }; Response.ErrorListener errorListener = new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { callback.onError(); } }; StringRequest requestRequest = new StringRequest(url, params, successListener, errorListener); LeSportsApplication.getApplication().addHttpRequest(requestRequest); }
请求的时候使用的StringRequest,请求成功后,会返回一个String类型,然后用Gson解析成JavaBean,我之前一直都这么使用,看似天衣无缝,其实你会发现JSon解析是在主线程中实现的,如果数据量大的话,很容易导致UI卡顿。优化方案就是在数据解析在子线程中进行
(1) 新建一个GsonRequest请求类,继承Request对象
(2) 重写parseNetworkResponse方法,这个方法其实是在NetworkDispatcher这个子线程中执行的
package com.lesports.common.volley.toolbox;
import android.os.Looper;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.lesports.common.volley.NetworkResponse;
import com.lesports.common.volley.ParseError;
import com.lesports.common.volley.Request;
import com.lesports.common.volley.Response;
import com.letv.core.log.Logger;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
/**
* Created by liuyu8 on 2015/9/15.
*/
public class MyGsonRequest<T> extends Request<T>{
private final Logger mLogger = new Logger("GsonRequest");
private final Gson gson = new Gson();
private final Class<T> clazz;
private Response.Listener<T> listener;
/**
* Make a GET request and return a parsed object from JSON. Assumes
* {@link Request.Method#GET}.
*
* @param url
* URL of the request to make
* @param clazz
* Relevant class object, for Gson's reflection
*/
public MyGsonRequest(String url,HashMap<String, String> params,Class<T> clazz, Response.Listener<T> listener, Response.ErrorListener errorListener) {
super(Request.Method.GET, url, params,errorListener);
this.clazz = clazz;
this.listener = listener;
}
/**
* Make a GET request and return a parsed object from JSON. Assumes
* {@link Request.Method#GET}.
*
* @param url
* URL of the request to make
* @param clazz
* Relevant class object, for Gson's reflection
*/
public MyGsonRequest(String url, Class<T> clazz, Response.Listener<T> listener, Response.ErrorListener errorListener) {
super(Request.Method.GET, url, errorListener);
this.clazz = clazz;
this.listener = listener;
}
/**
* Like the other, but allows you to specify which {@link Request.Method} you want.
*
* @param method
* @param url
* @param clazz
* @param listener
* @param errorListener
*/
public MyGsonRequest(int method, String url, Class<T> clazz, Response.Listener<T> listener, Response.ErrorListener errorListener) {
super(method, url, errorListener);
this.clazz = clazz;
this.listener = listener;
}
@Override
protected void deliverResponse(T response) {
if(listener != null){
listener.onResponse(response);
}
}
@Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
if(Looper.myLooper() == Looper.getMainLooper()){
mLogger.e("数据是 ==>在主线程中解析的~");
}else{
mLogger.e("数据不是 ==>在主线程中解析的~");
}
String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
JSONObject jsonObject = new JSONObject(json);
if(null != jsonObject && jsonObject.has("code") && jsonObject.getInt("code") == 200){
return Response.success(gson.fromJson(json, clazz), HttpHeaderParser.parseCacheHeaders(response));
}else{
return Response.error(new ParseError());
}
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JsonSyntaxException e) {
mLogger.e("JsonSyntaxException ==== ");
return Response.error(new ParseError(e));
} catch (JSONException e) {
return Response.error(new ParseError(e));
}
}
@Override
public void finish(final String tag) {
super.finish(tag);
listener = null;
}
}
(3) 然后进行二次封装
/** * 优化: * (1)避免在主线程中解析json数据 * (2)添加了取消请求方法 * * @param tag * @param url * @param params * @param clazz * @param callback * @param <T> */ protected <T> void doRequest(String tag, String url, HashMap<String, String> params, final Class<?> clazz, final HttpCallback callback) { String requestUrl = urlBuilder(url, params); Logger.e("BaseTVApi", requestUrl); callback.onLoading(); Response.Listener<T> successListener = new Response.Listener<T>() { @Override public void onResponse(T bean) { if (bean != null) { callback.onResponse(bean); Logger.e("BaseTVApi", "request-->onResponse"); } else { callback.onEmptyData(); Logger.e("BaseTVApi", "request-->onEmptyData"); } } }; Response.ErrorListener errorListener = new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { callback.onError(); Logger.e("BaseTVApi", "异常信息-->" + error.getMessage()); } }; GsonRequest requestRequest = new GsonRequest(url, params, clazz, successListener, errorListener); requestRequest.setTag(tag); LeSportsApplication.getApplication().addHttpRequest(requestRequest); }打印log你会发现,你会发现数据解析是在子线程中执行的。
(二) 使用Volley内存泄露
在用MAT做内存泄露检查的时候,发现由于Volley的回调没有干掉导致的泄露问题,解决方法就是保证在Activity退出时,cancel掉请求,Request方法有一个cancel和finish方法并在这2个方法中把listener置为空。
(1) 在Application中提供一个取消请求的方法,因为一般请求队列是在Application中初始化的。
/** * 网络请求优化,取消请求 * @param tag */ public void cancelRequest(String tag){ try { mRequestQueue.cancelAll(tag); }catch (Exception e){ Logger.e("LeSportsApplication","tag =="+ tag + "的请求取消失败"); } }(2) 在onStop中调用cancelRequest方法,最终会调用request的finish方法
(3) 在GsonRequest中重写finish方法,并在该方法中把listener置为空
@Override
public void finish(final String tag) {
super.finish(tag);
listener = null;
}
(4) 在再看看GsonRequest 的父类Request的finish方法,我已经把mErrorListener干掉了。
public void finish(final String tag) { if (mRequestQueue != null) { mRequestQueue.finish(this); } if (MarkerLog.ENABLED) { final long threadId = Thread.currentThread().getId(); if (Looper.myLooper() != Looper.getMainLooper()) { // If we finish marking off of the main thread, we need to // actually do it on the main thread to ensure correct ordering. Handler mainThread = new Handler(Looper.getMainLooper()); mainThread.post(new Runnable() { @Override public void run() { mEventLog.add(tag, threadId); mEventLog.finish(this.toString()); } }); return; } mEventLog.add(tag, threadId); mEventLog.finish(this.toString()); } else { long requestTime = SystemClock.elapsedRealtime() - mRequestBirthTime; if (requestTime >= SLOW_REQUEST_THRESHOLD_MS) { VolleyLog.d("%d ms: %s", requestTime, this.toString()); } } mErrorListener = null; }总结:请求取消最终都会调用Request的finish方法,我们只要在这个方法中,把2个回调给干掉,问题就解决了。
(三) 在onStop中取消请求
QA妹子给我报了一个奇葩的bug,具体是第一次打开应用的时候APP一直在loading,找了半天发现原来是由于是取消请求不当引起的。
原因是:我第一次进入应用的时候,立马弹出了一个升级对话框,这个时候刚好触发了onStop方法,自然请求就取消掉了,所以就一直在loading呗! 后来我就把该页面的请求放在onDestroy中cancel。
(四) 嵌套请求
一个页面中可能会有多个请求,有的请求要等到其它的请求完后才能进行。就像这样:
private void requestUserSubscribesGame() {
uid = LoginUtils.getUid();
if (StringUtils.isStringEmpty(uid)) {
return;
}
UserTVApi.getInstance().getUserSubscribeGameId(TAG, uid, new SimpleCallback<ApiBeans.SubScribeListModel>() {
@Override
public void onResponse(ApiBeans.SubScribeListModel bean) {
SubScribeModel subScribeModel = bean.data;
if (subScribeModel != null) {
scribeGameEntrys = subScribeModel.getEntities();
requestHallData();
}
}
@Override
public void onError() {
Logger.e(TAG, "获取订阅信息失败...");
}
});
}
我之前也很喜欢这样写,后来我查看log的时候发现,第一个请求会超时请求,导致会重试一次。我设置的超时时间是2秒,可能是由于第二个请求太慢,导致请求第一次回调的时候太长导致的吧,具体原因还需要去查。
解决方法:有嵌套请求的情况,建议第一个接口请求完后,用handler发送消息,然后第二个开始请求。
相关推荐
在本项目中,"使用Volley-jar的包"是一个包含了Volley库的Android工程,目的是验证打包的Volley库是否能够正常工作。以下是对Volley库及其使用的详细解释。 1. **Volley的特点**: - **快速响应**:Volley通过内存...
使用Volley StringRequest和JsonObjectRequest遇到的一些问题。博客地址:http://blog.csdn.net/onlysnail/article/details/47905375
### 使用 Volley 获取 JSON 数据 #### 一、简介 在 Android 开发中,应用程序常常需要与后端服务器进行交互,以获取或发送数据。其中一种常用的技术是使用 Volley 库来处理网络请求。Volley 是一个现代化的网络库...
在你的压缩包中,可能包含了使用Volley的示例代码(Dome)和详细的说明文档,这可以帮助你更深入地理解和应用Volley框架。通过阅读这些示例和文档,你将能够了解如何处理不同类型的网络请求,如何配置缓存策略,以及...
Volley以其高效、灵活和易于使用的特性,成为许多Android开发者首选的网络库。 首先,我们来详细了解一下Volley的核心特性: 1. **缓存策略**:Volley提供了内存和磁盘两级缓存机制,可以有效地减少网络请求,提高...
在本篇文章中,我们将深入探讨Volley的基本使用,包括其核心组件、网络请求的发起与处理,以及如何在实际项目中集成和配置Volley。 ### Volley的核心组件 1. **RequestQueue**:这是Volley的核心组件,负责管理...
"使用volley优化的瀑布流"这一主题,正是关注于如何通过Volley库来提升Android应用的性能,特别是对于图片加载和缓存的优化。Volley是Google推出的一个网络请求库,它设计的目标是快速、高效,特别适合于移动设备上...
在使用 Volley 进行网络请求时,可能会遇到中文乱码的问题。这通常与服务器返回的数据编码和客户端解析时的编码不一致有关。为了解决这个问题,我们可以在自定义的 Request 类中重写 `getParamsEncoding()` 和 `...
Volley是Android平台上的一个网络库,由Google开发并推荐使用,主要针对移动设备的特点进行了优化,特别适合处理大量的小数据量的网络请求。Volley提供了高效、易用且功能丰富的网络通信框架,使得开发者可以方便地...
在"使用Volley天气预报app"中,开发者利用Volley库来获取并显示天气预报信息。这个应用程序展示了如何有效地利用Volley的`StringRequest`和`JsonRequest`来处理不同类型的数据。 1. **Volley基础**: - **Volley...
这个“Volley使用的demo”是一个实际应用示例,演示了如何利用Volley框架来加载网络图片。下面我们将深入探讨Volley的关键特性和在Android应用程序中的使用方法。 ### 1. Volley简介 Volley设计的核心目标是速度和...
在“Volley使用例子”中,我们主要探讨如何使用Volley来加载ListView中的图片。 首先,Volley的核心在于其强大的请求队列管理。它通过将网络请求分发到线程池中执行,避免了主线程被阻塞,提高了用户体验。Volley...
在本“Volley封装使用Demo”中,我们将深入探讨如何利用Volley进行网络请求的封装,以实现高效、便捷的网络数据交互。 首先,Volley的核心组件包括RequestQueue(请求队列)、NetworkDispatcher(网络调度器)和...
在Android应用开发中,网络通信是必不可少的一部分,而Volley是一个高效的网络通信库,由Google设计并推荐使用。本文将详细解析如何使用Volley进行网络请求,包括GET、POST、图片加载、HTTPS通信、处理Cookie以及...
Android之Volley框架的使用,包括Volley的get和post请求方式的使用、Volley的取消队列网络请求和Volley与Activity生命周期的联动、Volley的简单的二次回调封装、Volley加载图片的介绍及用法
本文将深入探讨如何使用Volley框架通过GET方法从网络获取数据。 一、Volley概述 Volley是Android平台上的一个网络通信库,它的设计目标是简化网络请求并优化内存使用。Volley提供了异步网络请求、请求队列管理、...
Volley 是 Google 在 2013 年的 I/O 大会上推出的一个强大的网络通信库,专为 Android 应用设计,旨在简化网络请求处理并提供高效的图片加载功能。Volley 的核心设计理念是速度和易用性,它通过缓存机制和线程池优化...
com.android.volley:volley volley.jar
本文将详细介绍如何在Android项目中使用Volley库来实现图片轮播。 首先,我们需要在项目中集成Volley库。这可以通过在build.gradle文件中添加依赖来实现: ```gradle dependencies { implementation '...