在Android中,网络请求无非就这两种:HttpURLConnection和HttpClient( Apache),我们在使用时一般都会对它们进行一系列的封装,但是这过程不免有些繁琐,所以,Google官方也考虑到了这点,在2013年Google I/O大会上就推出了一个新的网络请求框架——Volley,它将各种网络请求都简单化,并且把AsyncHttpClient和Universal-Image-Loader两大框架的优点集一身,Volley用在数据量不大的网络请求操作时它的性能表现的非常出色,但是Volley如果在进行数据量大的网络操作时(下载文件等),那么Volley将表现的比较糟糕。

关于Volley的架构可以看看这个文章Volley架构

Volley有这么几大功能:

1、普通数据、JSON、图片的异步加载

2、网络请求优先级处理

3、自带硬盘缓存(普通数据、图片、JSON),另外我们在加载图片时候通过ImageLoader还可加入LruCache

4、取消请求

5、与Activity生命周期联动(Activity退出时同时取消所有的请求)

可见,Volley框架是非常强大的,下面我就一一介绍怎么使用Volley框架。

Volley框架的原理:它内部是通过一个请求队列(RequestQueue)来维护所有请求,我们新创建一个请求(request)后通过RequestQueue.add()方法将请求添加置请求队列中,然后调用RequestQueue.start()方法执行请求队列中的方法

Volley中包含这么几种类型的请求:

  1. StringRequest - 返回字符串数据
  2. JsonObjectRequest - 返回JSONArray数据
  3. JsonArrayRequest - 返回JSONObject数据
  4. ImageRequest - 返回Bitmap类型数据

当然使用前我们必须导入Volley.jar包(可以去网上下载),或者通过git下载

git clone https://android.googlesource.com/platform/frameworks/volley

这里给出我上传的jar包下载地址:Volley.jar

创建RequestQueue请求队列

RequestQueue是通过Volley的静态方法newRequestQueue来创建的:

RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());

一般我们会继承自Application在自定义的MyApplication中创建一个全局的请求队列,用来维护app中的网络请求。

StringRequest

这里主要讲最常用的GET和POST请求方式:

这里我用聚合网上查询手机号码归属地的数据为例子,我们创建一个StringRequest请求,然后给该请求设置一个Tag,用来标记这个请求,取消请求时候我们可以通过这个Tag来取消某个或者所有请求,再把该请求加入请求队列,最后执行请求队列中的请求。

StringRequest的构造方法为:

/**
* @method 请求方式(GET、POST等)
* @url 请求url
* @listener 请求成功回调的接口
* @errorListener 请求失败回调的接口
*/
public StringRequest(int method, String url, Listener<String> listener, ErrorListener errorListener)

GET

一个完整的StringRequest的GET请求如下:

        String url = "http://apis.juhe.cn/mobile/get?phone=18270837821&key=9a4329bdf84fa69d193ce601c22b949d";
        RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());//创建一个请求队列
        StringRequest request = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
            @Override
            public void onResponse(String s) {
                Toast.makeText(getApplicationContext(),s,Toast.LENGTH_SHORT).show();
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError volleyError) {
                Toast.makeText(getApplicationContext(),volleyError.toString(),Toast.LENGTH_SHORT).show();
            }
        });
        request.setTag("zxy");
        mRequestQueue.add(request);
        mRequestQueue.start();

POST

一个完整的StringRequest的POST请求如下:

String url = "http://apis.juhe.cn/mobile/get";
        RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        StringRequest request = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
            @Override
            public void onResponse(String s) {
                Toast.makeText(getApplicationContext(),s,Toast.LENGTH_SHORT).show();
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError volleyError) {
                Toast.makeText(getApplicationContext(),volleyError.toString(),Toast.LENGTH_SHORT).show();
            }
        }){
            @Override
            protected Map<String, String> getParams() throws AuthFailureError {
                Map<String,String> map  =new HashMap<>();
                map.put("phone","18270837821");
                map.put("key","9a4329bdf84fa69d193ce601c22b949d");
                return map;
            }
        };
        request.setTag("zxy");
        mRequestQueue.add(request);
        mRequestQueue.start();

其中,因为是以POST方式请求数据的,所以我们必须实现StringRequest的getParams()方法,该方法返回的是Map<String, String>类型的集合,也就是用<key,value>的形式把数据通过POST传入服务器

JsonObjectRequest

JsonObjectRequest构造方法为:

/**
* @method 请求方式(GET、POST等)
* @url 请求url
* @jsonRequest 请求传入的json数据
* @listener 请求成功回调的接口
* @errorListener 请求失败回调的接口
*/
public JsonObjectRequest(int method, String url, JSONObject jsonRequest, Listener<JSONObject> listener, ErrorListener errorListener)

GET

一个完整的JsonObjectRequest的GET请求如下:

因为用的是GET请求方式,参数是在url中传入,所以JSONObject对象传入null

String url = "http://apis.juhe.cn/mobile/get?phone=18270837821&key=9a4329bdf84fa69d193ce601c22b949d";
        RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject jsonObject) {
                Toast.makeText(getApplicationContext(),jsonObject.toString(),Toast.LENGTH_SHORT).show();
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError volleyError) {
                Toast.makeText(getApplicationContext(),volleyError.toString(),Toast.LENGTH_SHORT).show();
            }
        });

        request.setTag("zxy");
        mRequestQueue.add(request);
        mRequestQueue.start();

POST

一个完整的JsonObjectRequest的POST请求如下:

String url = "http://apis.juhe.cn/mobile/get";
        RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put("phone", "18270837821");
            jsonObject.put("key", "9a4329bdf84fa69d193ce601c22b949d");
        } catch (JSONException e) {
            e.printStackTrace();
        }
        JsonObjectRequest request = new JsonObjectRequest(Request.Method.POST, url, jsonObject, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject jsonObject) {
                Toast.makeText(getApplicationContext(),jsonObject.toString(),Toast.LENGTH_SHORT).show();
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError volleyError) {
                Toast.makeText(getApplicationContext(),volleyError.toString(),Toast.LENGTH_SHORT).show();
            }
        });
        request.setTag("zxy");
        mRequestQueue.add(request);
        mRequestQueue.start();

用以上StringRequest和JSONObjectRequest请求我们都获取到了数据,如图

虽然这两种方式都可以返回我们请求的数据,但是JSONObjectRequest请求在处理json对象的返回结果时候效率更高,所以确定返回结果是json类型时候可以使用JSONObjectRequest

ImageRequest

ImageRequest的构造方法为:

/**
* @url 请求url
* @listener 请求成功回调的接口
* @maxWidth 图片最大的宽度(如果超过则Volley会对图片进行压缩,如果为0则不压缩)
* @maxHeight 图片最大的高度
* @decodeConfig 图片的配置
* @errorListener 请求失败回调的接口
*/
public ImageRequest(String url, Listener<Bitmap> listener, int maxWidth, int maxHeight, Config decodeConfig, ErrorListener errorListener)

普通的加载方式(ImageView显示)

一个标准的普通请求网络图片的方法:

String url="http://img.my.csdn.net/uploads/201507/21/1437459520_6685.jpg";
        RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        ImageRequest request = new ImageRequest(url, new Response.Listener<Bitmap>() {
            @Override
            public void onResponse(Bitmap bitmap) {
                mImageView.setImageBitmap(bitmap);
            }
        }, 0, 0, Bitmap.Config.ARGB_8888, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError volleyError) {
                Toast.makeText(getApplicationContext(),volleyError.toString(),Toast.LENGTH_SHORT).show();
            }
        });
        request.setTag("zxy");
        mRequestQueue.add(request);
        mRequestQueue.start();
    }

这种方式只适合在图片数量不多的情况下使用,否则则需要考虑使用下面两种,同样,因为Volley内部本身就有硬盘缓存机制,在没网的情况下则会加载缓存中的图片

带LruCache缓存的加载方式(ImageView+ImageLoader+ImageCache)

我们上一种方法就是使用Volley中的ImageRequest请求的加载网络图片,这种方法没有LruCache和显示效果不好,下面我们来使用Volley中的ImageLoader+ImageCache的加载方式,这种方式可以在网络差的情况下和加载出错的情况下给出一个默认的提示图片,而且使用了LruCache缓存可以避免OOM。

首先我们创建一个ImageLoader对象,ImageLoader构造方法

public ImageLoader(RequestQueue queue, ImageLoader.ImageCache imageCache)

需要传入请求队列对象和ImageCache对象,我们再来看看ImageCache对象

public interface ImageCache{...}

发现它是ImageLoader内部的一个接口,所以我们得实现这个接口然后传入,于是我们创建一个BimapCache实现ImageCache接口:

public class BitmapCache implements ImageLoader.ImageCache {
    private LruCache<String, Bitmap> mBitmapLruCache;

    public BitmapCache() {
        int maxCache = (int) Runtime.getRuntime().maxMemory();
        int cacheSize = maxCache / 8;
        mBitmapLruCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getByteCount();
            }
        };
    }

    @Override
    public Bitmap getBitmap(String key) {
        return mBitmapLruCache.get(key);
    }

    @Override
    public void putBitmap(String key, Bitmap bitmap) {
        mBitmapLruCache.put(key, bitmap);
    }
}

其实这就是创建一个图片内存缓存对象。

之后我们再使用ImageLoader.get()方法加载网络图片,我们来看看get()方法的参数:

/**
* @requestUrl - 请求url
* @listener - ImageLoader.ImageListener的监听对象
* @maxWidth - 图片的最大高度,如果超过则会压缩,为0则不压缩
* @maxHeight
*/
public ImageLoader.ImageContainer get(String requestUrl, ImageLoader.ImageListener listener, int maxWidth, int maxHeight)

第一个参数好办,那么第二个参数我们可以通过ImageLoader.getImageListener()来得到,我们来看看它的参数:

/**
* @view - 表示将图片设置到哪个控件对象上
* @defaultImageResId- 默认时显示的图片的资源id
* @errorImageResId- 加载出错时显示的图片的资源id
*/
public static ImageLoader.ImageListener getImageListener(final ImageView view, final int defaultImageResId, final int errorImageResId)

好了一切都好办了,一个带LruCache缓存的加载图片的标准请求:

String url="http://img.my.csdn.net/uploads/201507/21/1437459520_6685.jpg";
        RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        ImageLoader mImageLoader = new ImageLoader(mRequestQueue,new BitmapCache());
        ImageLoader.ImageListener imageListener = ImageLoader.getImageListener(mImageView, R.mipmap.ic_launcher, R.mipmap.ic_launcher);
        mImageLoader.get(url,imageListener,0,0);

效果图:

【注意】:mImageLoader.get(url,imageListener)加载图片的原理是什么呢?其实从源码中很容易看出,它首先会得到url对应的key,然后判断硬盘缓存中是否含有该key的图片,如果有则取出,没有则通过网络请求重新加载图片,所以使用这种双缓存的方式加载网络图片,可以有效的防止OOM

使用NetworkImageView+ImageLoader+ImageCache

Volley框架中对图片的请求做的特别好,其中还为我们提供了一个专门用于显示图片的控件:NetworkImageView,该控件继承自ImageView,除了拥有ImageView控件的功能之外,还多了三个方法:

public void setImageUrl(String url, ImageLoader imageLoader) {
        this.mUrl = url;
        this.mImageLoader = imageLoader;
        this.loadImageIfNecessary(false);
    }

public void setDefaultImageResId(int defaultImage) {
        this.mDefaultImageId = defaultImage;
    }

public void setErrorImageResId(int errorImage) {
        this.mErrorImageId = errorImage;
    }
  1. setDefaultImageResId - 设置该控件默认时显示的图片
  2. setErrorImageResId - 设置加载网络图片失败时显示的图片
  3. setImageUrl - 从网络上加载图片

使用NetworkImageView需要在layout中替换掉ImageView:

<com.android.volley.toolbox.NetworkImageView
        android:id="@+id/net_img"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

同样这里也需要使用ImageLoader和ImageCache,一个标准使用NetworkImageView的例子为:

String url="http://img.my.csdn.net/uploads/201507/21/1437459520_6685.jpg";
        RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        ImageLoader mImageLoader = new ImageLoader(mRequestQueue,new BitmapCache());
        mNetworkImageView.setDefaultImageResId(R.mipmap.ic_launcher);//设置默认显示的图片
        mNetworkImageView.setErrorImageResId(R.mipmap.ic_launcher);//设置加载出错时显示的图片
        mNetworkImageView.setImageUrl(url,mImageLoader);

效果为:



可以看到用NetworkImageView和第二种方式效果是一样的,它同样会先检查硬盘缓存中有没有该图片,如果没有,再通过网络加载得到该图片,如果有则直接设置。既然和第二种一样,那为什么还推出NetworkImageView这个控件呢?答案就是NetworkImageView这个控件在你的Activity退出时候会自动取消网络请求,即完全不需要我们担心网络请求生命周期的问题。

【注意】:在上述两种使用了LruCache缓存加载图片的方法,当图片量较大时推荐使用,否则当你只有几张图片则使用第一种比较好,因为你使用LruCache时需要为它分配一定的内存空间,而图片量不大时候也使用LruCache缓存,那这块空间则一直是作为缓存图片用的,占着这块内存空间,相反反而会得不偿失。

Volley与Activity生命周期联动与取消请求

其实就是在Activity退出时候或销毁时候,取消对应的网络请求,避免网络请求在后台浪费资源,所以,我们一般在onStop()方法中通过之前设置的Tag取消网络请求:

@Override
protected void onStop() {
        super.onStop();
        mRequestQueue.cancelAll("zxy");
    }

RequestQueue.cancelAll()方法会过滤出Tag为“zxy”的所有请求都一并取消。

或者通过Request.cancel()取消请求,把当前Activity的请求放入一个List集合中,关闭Activity时分别取消。

好了,这篇Volley框架的使用篇已经讲完了,下一篇讲Volley的缓存!!!

Volley网络框架完全解析(使用篇)的更多相关文章

  1. Volley网络框架完全解析(缓存篇)

    在上一篇中讲完了Volley框架怎么使用,那么这篇就来讲讲Volley框架的缓存机制 我们看Volley内部源码发现: Volley框架内部自己处理了DiskBasedCache硬盘缓存,但是没有处理 ...

  2. Volley网络框架完全解析(实战篇)

    好了,今天就通过一个瀑布流demo,来使用Volley框架请求网络图片. 前言: 我们使用NetworkImageView显示图片: 1.因为该控件可以自动的管理好请求的生命周期,当与父控件detac ...

  3. 谷歌Volley网络框架讲解——BasicNetwork类

    谷歌Volley网络框架讲解——BasicNetwork类 这个类是toolbox工具箱包里的,实现了Network接口. 先来看下Network这个interface,performRequest( ...

  4. 谷歌Volley网络框架讲解——第一篇

    自从公司新招了几个android工程师后,我清闲了些许.于是就可以有时间写写博客,研究一些没来的研究的东西. 今年的谷歌IO大会上,谷歌推出了自己的网络框架——Volley.不久前就听说了但是没有cl ...

  5. Volley网络框架的使用

    Volley的特点:   使用网络通信更快.更简单 Get/Post网络请求网络图像的高效率异步请求 可以对网络请求的优先级进行排序处理 可以进行网络请求的缓存 可以取消多级别请求 可以和Activi ...

  6. 谷歌Volley网络框架讲解——HttpStack及其实现类

    前两篇已经对网络请求流程已经梳理了个大概,这次我们着重看一下HttpStack和它的其实现类.我们之前在Network篇讲过它仅有一个实现类,而今天我们讲的HttpStack有两个实现类. 其中Htt ...

  7. 谷歌Volley网络框架讲解——网络枢纽

    研究了这么久的Volley,愈来愈发现这个框架的精美和人性化.比起民间一些框架强很多,一开始总是盲人摸象找不到头绪,现在终于有些明朗了.Volley其实就是一个请求队列的代理类,我们看下UML. 这就 ...

  8. 谷歌Volley网络框架讲解——Network及其实现类

    我们看到Network接口只有一个实现类BasicNetwork,而HttpStack有两个实现类. BasicNetwork这个类是toolbox工具箱包里的,实现了Network接口. 先来看下N ...

  9. Android网络框架Volley(体验篇)

    Volley是Google I/O 2013推出的网络通信库,在volley推出之前我们一般会选择比较成熟的第三方网络通信库,如: android-async-http retrofit okhttp ...

随机推荐

  1. java new 关键字到底做了什么?

    一.关键字new概述 "new"可以说是Java开发者最常用的关键字,我们使用new创建对象,使用new并通过类加载器来实例化任何我们需要的东西,但你是否深入了解过new在编译的瞬 ...

  2. DDL/DML/DCL区别概述

    DDL DDL的概述 DDL(Data Definition Language 数据定义语言)用于操作对象和对象的属性,这种对象包括数据库本身,以及数据库对象,像:表.视图等等,DDL对这些对象和属性 ...

  3. Java网络爬虫Hello world实现——Httpclient爬取百度首页

    1.创建Maven项目 2.Httpclient Maven地址 <dependency> <groupId>org.apache.httpcomponents</gro ...

  4. 009.Working with SQL Server LocalDB --【在sql server localdb 上操作数据】

    Working with SQL Server LocalDB 在sql server localdb 上操作数据 2017-3-7 2 分钟阅读时长 本文内容 1.SQL Server Expres ...

  5. 关于mysql安装到最后一步老是停留在starting server,显示无响应

    从昨天晚上到今天安装MySQL花了好长的时间,一直是在后面starting server 这部就显示无响应,查资料了解到是MySQL有残留,有些注册表文件需要手动清理,下面是具体方法. 1.先用卸载软 ...

  6. tomcat连接池配置和使用

    一种方法是在conf/context.xml文件中配置,配置oracle连接池的一个例子的context内容如下: <?xml version='1.0' encoding='utf-8'?&g ...

  7. 智能指针之 unique_ptr

    对于动态申请的内存,C++语言为我们提供了new和delete运算符, 而没有像java一样,提供一个完整的GC机制,因此对于我们申请的动态内存, 我们需要时刻记得释放,且不能重复释放,释放后不能再去 ...

  8. LintCode题解之统计数字

    直接硬搜就可以了,只是需要考虑k为0的情况. public class Solution { /* * @param : An integer * @param : An integer * @ret ...

  9. Django的配置文件(settings)

    静态文件设置: 一.概述: #静态文件交由Web服务器处理,Django本身不处理静态文件.简单的处理逻辑如下(以nginx为例): # URI请求-----> 按照Web服务器里面的配置规则先 ...

  10. OpenResty和Resis一些基本的性能配置

    Basics: 1. Ensure that you have not disabled Lua code cache: https://github.com/openresty/lua-nginx- ...