那些年我使用Volley遇到的坑

转自:http://1029457926.iteye.com/blog/2264167


    使用Volery已经快整整一年了,下面我来总结一下,我使用Volley时踩到的坑

     (一) Volley的二次封装

    下面看看我是怎么对Volley的二次封装的:
Java代码   收藏代码
  1. protected <T> void doSimpleRequest(String url, HashMap<String, String> params, final Class<?> clazz, final SimpleCallback callback) {  
  2.       String requestUrl = urlBuilder(url, params);  
  3.       Logger.e("url", requestUrl);  
  4.       Response.Listener<String> successListener = new Response.Listener<String>() {  
  5.           @Override  
  6.           public void onResponse(String response) {  
  7.               try {  
  8.                   T bean = GsonUtils.getFromStr(clazz, response);  
  9.                   callback.onResponse(bean);  
  10.               } catch (Exception e) {  
  11.                   callback.onError();  
  12.               }  
  13.           }  
  14.       };  
  15.       Response.ErrorListener errorListener = new Response.ErrorListener() {  
  16.           @Override  
  17.           public void onErrorResponse(VolleyError error) {  
  18.               callback.onError();  
  19.           }  
  20.       };  
  21.       StringRequest requestRequest = new StringRequest(url, params, successListener, errorListener);  
  22.       LeSportsApplication.getApplication().addHttpRequest(requestRequest);  
  23.   }  
 
      请求的时候使用的StringRequest,请求成功后,会返回一个String类型,然后用Gson解析成JavaBean,我之前一直都这么使用,看似天衣无缝,其实你会发现JSon解析是在主线程中实现的,如果数据量大的话,很容易导致UI卡顿。优化方案就是在数据解析在子线程中进行
(1) 新建一个GsonRequest请求类,继承Request对象
(2) 重写parseNetworkResponse方法,这个方法其实是在CacheDispatch这个子线程中执行的
Java代码   收藏代码
  1. <span style="font-size: 18px;">package com.lesports.common.volley.toolbox;  
  2. import android.os.Looper;  
  3. import com.google.gson.Gson;  
  4. import com.google.gson.JsonSyntaxException;  
  5. import com.lesports.common.volley.NetworkResponse;  
  6. import com.lesports.common.volley.ParseError;  
  7. import com.lesports.common.volley.Request;  
  8. import com.lesports.common.volley.Response;  
  9. import com.letv.core.log.Logger;  
  10. import org.json.JSONException;  
  11. import org.json.JSONObject;  
  12. import java.io.UnsupportedEncodingException;  
  13. import java.util.HashMap;  
  14. /** 
  15.  * Created by liuyu8 on 2015/9/15. 
  16.  */  
  17. public class MyGsonRequest<T> extends Request<T>{  
  18.     private final Logger mLogger = new Logger("GsonRequest");  
  19.     private final Gson gson = new Gson();  
  20.     private final Class<T> clazz;  
  21.     private Response.Listener<T> listener;  
  22.     /** 
  23.      * Make a GET request and return a parsed object from JSON. Assumes 
  24.      * {@link Request.Method#GET}. 
  25.      * 
  26.      * @param url 
  27.      *            URL of the request to make 
  28.      * @param clazz 
  29.      *            Relevant class object, for Gson's reflection 
  30.      */  
  31.     public MyGsonRequest(String url,HashMap<String, String> params,Class<T> clazz, Response.Listener<T> listener, Response.ErrorListener errorListener) {  
  32.         super(Request.Method.GET, url, params,errorListener);  
  33.         this.clazz = clazz;  
  34.         this.listener = listener;  
  35.     }  
  36.     /** 
  37.      * Make a GET request and return a parsed object from JSON. Assumes 
  38.      * {@link Request.Method#GET}. 
  39.      * 
  40.      * @param url 
  41.      *            URL of the request to make 
  42.      * @param clazz 
  43.      *            Relevant class object, for Gson's reflection 
  44.      */  
  45.     public MyGsonRequest(String url, Class<T> clazz, Response.Listener<T> listener, Response.ErrorListener errorListener) {  
  46.         super(Request.Method.GET, url, errorListener);  
  47.         this.clazz = clazz;  
  48.         this.listener = listener;  
  49.     }  
  50.     /** 
  51.      * Like the other, but allows you to specify which {@link Request.Method} you want. 
  52.      * 
  53.      * @param method 
  54.      * @param url 
  55.      * @param clazz 
  56.      * @param listener 
  57.      * @param errorListener 
  58.      */  
  59.     public MyGsonRequest(int method, String url, Class<T> clazz, Response.Listener<T> listener, Response.ErrorListener errorListener) {  
  60.         super(method, url, errorListener);  
  61.         this.clazz = clazz;  
  62.         this.listener = listener;  
  63.     }  
  64.     @Override  
  65.     protected void deliverResponse(T response) {  
  66.         if(listener != null){  
  67.             listener.onResponse(response);  
  68.         }  
  69.     }  
  70.     @Override  
  71.     protected Response<T> parseNetworkResponse(NetworkResponse response) {  
  72.         try {  
  73.             if(Looper.myLooper() == Looper.getMainLooper()){  
  74.                 mLogger.e("数据是 ==>在主线程中解析的~");  
  75.             }else{  
  76.                 mLogger.e("数据不是 ==>在主线程中解析的~");  
  77.             }  
  78.             String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));  
  79.             JSONObject jsonObject = new JSONObject(json);  
  80.             if(null != jsonObject && jsonObject.has("code") && jsonObject.getInt("code") == 200){  
  81.                 return Response.success(gson.fromJson(json, clazz), HttpHeaderParser.parseCacheHeaders(response));  
  82.             }else{  
  83.                 return Response.error(new ParseError());  
  84.             }  
  85.         } catch (UnsupportedEncodingException e) {  
  86.             return Response.error(new ParseError(e));  
  87.         } catch (JsonSyntaxException e) {  
  88.             mLogger.e("JsonSyntaxException ==== ");  
  89.             return Response.error(new ParseError(e));  
  90.         } catch (JSONException e) {  
  91.             return Response.error(new ParseError(e));  
  92.         }  
  93.     }  
  94.     @Override  
  95.     public void finish(final String tag) {  
  96.         super.finish(tag);  
  97.         listener = null;  
  98.     }  
  99. }</span>  
 (3) 然后进行二次封装
Java代码   收藏代码
  1. /** 
  2.      * 优化: 
  3.      * (1)避免在主线程中解析json数据 
  4.      * (2)添加了取消请求方法 
  5.      * 
  6.      * @param tag 
  7.      * @param url 
  8.      * @param params 
  9.      * @param clazz 
  10.      * @param callback 
  11.      * @param <T> 
  12.      */  
  13.     protected <T> void doRequest(String tag, String url, HashMap<String, String> params, final Class<?> clazz, final HttpCallback callback) {  
  14.         String requestUrl = urlBuilder(url, params);  
  15.         Logger.e("BaseTVApi", requestUrl);  
  16.         callback.onLoading();  
  17.         Response.Listener<T> successListener = new Response.Listener<T>() {  
  18.             @Override  
  19.             public void onResponse(T bean) {  
  20.                 if (bean != null) {  
  21.                     callback.onResponse(bean);  
  22.                     Logger.e("BaseTVApi""request-->onResponse");  
  23.                 } else {  
  24.                     callback.onEmptyData();  
  25.                     Logger.e("BaseTVApi""request-->onEmptyData");  
  26.                 }  
  27.             }  
  28.         };  
  29.         Response.ErrorListener errorListener = new Response.ErrorListener() {  
  30.             @Override  
  31.             public void onErrorResponse(VolleyError error) {  
  32.                 callback.onError();  
  33.                 Logger.e("BaseTVApi""异常信息-->" + error.getMessage());  
  34.             }  
  35.         };  
  36.         GsonRequest requestRequest = new GsonRequest(url, params, clazz, successListener, errorListener);  
  37.         requestRequest.setTag(tag);  
  38.         LeSportsApplication.getApplication().addHttpRequest(requestRequest);  
  39.     }  
  打印log你会发现,你会发现数据解析是在子线程中执行的。

(二) 使用Volley内存泄露

    在用MAT做内存泄露检查的时候,发现由于Volley的回调没有干掉导致的泄露问题,解决方法就是保证在Activity退出时,cancel掉请求,Request方法有一个cancel和finish方法并在这2个方法中把listener置为空。
 (1) 在Application中提供一个取消请求的方法,因为一般请求队列是在Application中初始化的。
Java代码   收藏代码
  1. /** 
  2.    * 网络请求优化,取消请求 
  3.    * @param tag 
  4.    */  
  5.   public  void  cancelRequest(String tag){  
  6.       try {  
  7.           mRequestQueue.cancelAll(tag);  
  8.       }catch (Exception e){  
  9.           Logger.e("LeSportsApplication","tag =="+ tag + "的请求取消失败");  
  10.       }  
  11.   }  
(2) 在onStop中调用cancelRequest方法,最终会调用request的finish方法
(3) 在GsonRequest中重写finish方法,并在该方法中把listener置为空
Java代码   收藏代码
  1. <span style="font-size: 18px;">  @Override  
  2.     public void finish(final String tag) {  
  3.         super.finish(tag);  
  4.         listener = null;  
  5.     }</span>  
  (4) 在再看看GsonRequest 的父类Request的finish方法,我已经把mErrorListener干掉了。
Java代码   收藏代码
  1. public void finish(final String tag) {  
  2.         if (mRequestQueue != null) {  
  3.             mRequestQueue.finish(this);  
  4.         }  
  5.         if (MarkerLog.ENABLED) {  
  6.             final long threadId = Thread.currentThread().getId();  
  7.             if (Looper.myLooper() != Looper.getMainLooper()) {  
  8.                 // If we finish marking off of the main thread, we need to  
  9.                 // actually do it on the main thread to ensure correct ordering.  
  10.                 Handler mainThread = new Handler(Looper.getMainLooper());  
  11.                 mainThread.post(new Runnable() {  
  12.                     @Override  
  13.                     public void run() {  
  14.                         mEventLog.add(tag, threadId);  
  15.                         mEventLog.finish(this.toString());  
  16.                     }  
  17.                 });  
  18.                 return;  
  19.             }  
  20.             mEventLog.add(tag, threadId);  
  21.             mEventLog.finish(this.toString());  
  22.         } else {  
  23.             long requestTime = SystemClock.elapsedRealtime() - mRequestBirthTime;  
  24.             if (requestTime >= SLOW_REQUEST_THRESHOLD_MS) {  
  25.                 VolleyLog.d("%d ms: %s", requestTime, this.toString());  
  26.             }  
  27.         }  
  28.         mErrorListener = null;  
  29.     }  
     总结:请求取消最终都会调用Request的finish方法,我们只要在这个方法中,把2个回调给干掉,问题就解决了。

(三) 在onStop中取消请求

    QA妹子给我报了一个奇葩的bug,具体是第一次打开应用的时候APP一直在loading,找了半天发现原来是由于是取消请求不当引起的。
   原因是:我第一次进入应用的时候,立马弹出了一个升级对话框,这个时候刚好触发了onStop方法,自然请求就取消掉了,所以就一直在loading呗! 后来我就把该页面的请求放在onDestroy中cancel。

(四) 嵌套请求

  一个页面中可能会有多个请求,有的请求要等到其它的请求完后才能进行。就像这样:
Java代码   收藏代码
  1. <span style="font-size: 18px;"private void requestUserSubscribesGame() {  
  2.         uid = LoginUtils.getUid();  
  3.         if (StringUtils.isStringEmpty(uid)) {  
  4.             return;  
  5.         }  
  6.         UserTVApi.getInstance().getUserSubscribeGameId(TAG, uid, new SimpleCallback<ApiBeans.SubScribeListModel>() {  
  7.             @Override  
  8.             public void onResponse(ApiBeans.SubScribeListModel bean) {  
  9.                 SubScribeModel subScribeModel = bean.data;  
  10.                 if (subScribeModel != null) {  
  11.                     scribeGameEntrys = subScribeModel.getEntities();  
  12.                     requestHallData();  
  13.                 }  
  14.             }  
  15.             @Override  
  16.             public void onError() {  
  17.                 Logger.e(TAG, "获取订阅信息失败...");  
  18.             }  
  19.         });  
  20.     }</span>  
 
      我之前也很喜欢这样写,后来我查看log的时候发现,第一个请求会超时请求,导致会重试一次。我设置的超时时间是2秒,可能是由于第二个请求太慢,导致请求第一次回调的时候太长导致的吧,具体原因还需要去查。
    解决方法:有嵌套请求的情况,建议第一个接口请求完后,用handler发送消息,然后第二个开始请求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值