`
砺雪凝霜
  • 浏览: 156596 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

那些年我使用Volley遇到的坑

阅读更多

                               那些年我使用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发送消息,然后第二个开始请求。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
0
分享到:
评论
1 楼 zhu_jinlong 2016-01-31  
Volley持有context引用导致的memory leak实际上是很严重的,很多Volley的使用者并没有关注到这个问题,我是采用EventBus把结果Post回Activity/Fragment来解决的,并在OnDestory()里UnRegister EventBus.

相关推荐

    使用Volley-jar的包

    在本项目中,"使用Volley-jar的包"是一个包含了Volley库的Android工程,目的是验证打包的Volley库是否能够正常工作。以下是对Volley库及其使用的详细解释。 1. **Volley的特点**: - **快速响应**:Volley通过内存...

    Volley 使用遇到的种种问题

    使用Volley StringRequest和JsonObjectRequest遇到的一些问题。博客地址:http://blog.csdn.net/onlysnail/article/details/47905375

    使用volley获取json数据

    ### 使用 Volley 获取 JSON 数据 #### 一、简介 在 Android 开发中,应用程序常常需要与后端服务器进行交互,以获取或发送数据。其中一种常用的技术是使用 Volley 库来处理网络请求。Volley 是一个现代化的网络库...

    volley框架的使用

    在你的压缩包中,可能包含了使用Volley的示例代码(Dome)和详细的说明文档,这可以帮助你更深入地理解和应用Volley框架。通过阅读这些示例和文档,你将能够了解如何处理不同类型的网络请求,如何配置缓存策略,以及...

    android最新网络框架Volley使用实例

    Volley以其高效、灵活和易于使用的特性,成为许多Android开发者首选的网络库。 首先,我们来详细了解一下Volley的核心特性: 1. **缓存策略**:Volley提供了内存和磁盘两级缓存机制,可以有效地减少网络请求,提高...

    Android网络框架-Volley(一) 初步使用Volley

    在本篇文章中,我们将深入探讨Volley的基本使用,包括其核心组件、网络请求的发起与处理,以及如何在实际项目中集成和配置Volley。 ### Volley的核心组件 1. **RequestQueue**:这是Volley的核心组件,负责管理...

    使用volley优化的瀑布流(android)

    "使用volley优化的瀑布流"这一主题,正是关注于如何通过Volley库来提升Android应用的性能,特别是对于图片加载和缓存的优化。Volley是Google推出的一个网络请求库,它设计的目标是快速、高效,特别适合于移动设备上...

    volley 获取json数据

    在使用 Volley 进行网络请求时,可能会遇到中文乱码的问题。这通常与服务器返回的数据编码和客户端解析时的编码不一致有关。为了解决这个问题,我们可以在自定义的 Request 类中重写 `getParamsEncoding()` 和 `...

    android Volley的基本使用详解

    Volley是Android平台上的一个网络库,由Google开发并推荐使用,主要针对移动设备的特点进行了优化,特别适合处理大量的小数据量的网络请求。Volley提供了高效、易用且功能丰富的网络通信框架,使得开发者可以方便地...

    使用Volley 天气预报app

    在"使用Volley天气预报app"中,开发者利用Volley库来获取并显示天气预报信息。这个应用程序展示了如何有效地利用Volley的`StringRequest`和`JsonRequest`来处理不同类型的数据。 1. **Volley基础**: - **Volley...

    volley使用的demo

    这个“Volley使用的demo”是一个实际应用示例,演示了如何利用Volley框架来加载网络图片。下面我们将深入探讨Volley的关键特性和在Android应用程序中的使用方法。 ### 1. Volley简介 Volley设计的核心目标是速度和...

    volley使用例子

    在“Volley使用例子”中,我们主要探讨如何使用Volley来加载ListView中的图片。 首先,Volley的核心在于其强大的请求队列管理。它通过将网络请求分发到线程池中执行,避免了主线程被阻塞,提高了用户体验。Volley...

    Volley封装使用Demo

    在本“Volley封装使用Demo”中,我们将深入探讨如何利用Volley进行网络请求的封装,以实现高效、便捷的网络数据交互。 首先,Volley的核心组件包括RequestQueue(请求队列)、NetworkDispatcher(网络调度器)和...

    Android-网络通信框架Volley使用详解

    在Android应用开发中,网络通信是必不可少的一部分,而Volley是一个高效的网络通信库,由Google设计并推荐使用。本文将详细解析如何使用Volley进行网络请求,包括GET、POST、图片加载、HTTPS通信、处理Cookie以及...

    Android Volley框架的基本使用解析

    Android之Volley框架的使用,包括Volley的get和post请求方式的使用、Volley的取消队列网络请求和Volley与Activity生命周期的联动、Volley的简单的二次回调封装、Volley加载图片的介绍及用法

    使用Volley框架GET从网络上获取数据

    本文将深入探讨如何使用Volley框架通过GET方法从网络获取数据。 一、Volley概述 Volley是Android平台上的一个网络通信库,它的设计目标是简化网络请求并优化内存使用。Volley提供了异步网络请求、请求队列管理、...

    Android Volley框架使用实例

    Volley 是 Google 在 2013 年的 I/O 大会上推出的一个强大的网络通信库,专为 Android 应用设计,旨在简化网络请求处理并提供高效的图片加载功能。Volley 的核心设计理念是速度和易用性,它通过缓存机制和线程池优化...

    com.android.volley:volley volley.jar

    com.android.volley:volley volley.jar

    Android使用Volley实现图片轮播

    本文将详细介绍如何在Android项目中使用Volley库来实现图片轮播。 首先,我们需要在项目中集成Volley库。这可以通过在build.gradle文件中添加依赖来实现: ```gradle dependencies { implementation '...

Global site tag (gtag.js) - Google Analytics