Android HTTP网络通信(二):Volley框架——介绍、基本用法

本文深入探讨了Volley在Android平台上的网络通信应用,介绍了其核心功能,包括异步下载JSON和图像、网络请求的排序与优先级处理、缓存机制、多级取消请求和与Activity生命周期的联动。详细演示了如何使用StringRequest、JsonRequest和ImageRequest发起网络请求,以及如何使用ImageLoader加载网络图片。同时,文章强调了Volley在性能方面的优势和局限性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

什么是Voliey

发布时间:Google I/O 2013时期

Volley是Android平台上的网络通信库,能使网络通信更快,更简单,更健壮。Volley可是说是把AsyncHttpClient和Universal-Image-Loader的优点集于了一身,既可以像AsyncHttpClient一样非常简单地进行HTTP通信,也可以像Universal-Image-Loader一样轻松加载网络上的图片。

Volley在性能方面进行了大幅度的调整,它的设计目标就是非常适合去进行数据量不大通信频繁的网络操作,而对于大数据量的网络操作(大数据、流媒体),Volley的表现就有些糟糕。

Volley功能

  • JSON,图像等的异步下载
  • 网络请求的排序(scheduling)
  • 网络请求的优先级处理
  • 缓存
  • 多级别取消请求
  • 和Activity和生命周期的联动(Activity结束时同时取消所有网络请求)

下载Volley

首先,从git库先克隆一个下来,然后编译为jar包,再import到自己Android项目中。

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

也可以直接使用已有的jar包

下载地址:http://download.youkuaiyun.com/detail/dengchenhe/8511119

然后将下载后的.jar文件导入到Android项目中

StringRequest的用法

现在,我们发起一条HTTP请求,然后接收HTTP响应。首先需要获取到一个RequestQueue对象,如下代码:

RequestQueue mQueue = Volley.newRequestQueue(context);

注意这里得到的RequestQueue是一个请求队列对象,它可以缓存所有的HTTP请求,然后按照一定的算法并发地发出这些请求。RequestQueue内部的设计就是非常合适高并发的,因此我们不必为每一次HTTP请求都创建一个RequestQueue对象,这样会非常浪费资源,因此在每一个需要进行网络通信的Activity中创建一个RequestQueue对象就足够了。

如果要发出一条HTTP请求,我们还需要创建一个StringRequest对象,如下代码:

StringRequest stringRequest = new StringRequest("http://www.baidu.com",
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        Log.d(TAG, response);
                    }
                }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Log.e(TAG, error.getMessage(), error); 
                    }
                });

分析如上代码,这里new了一个StringRequest对象,StringRequest的构造函数需要传入三个参数,第一个参数就是目标服务器的URL地址,第二个参数是服务器响应成功的回调,第三个参数是服务器响应失败的回调。其中,目标服务器地址我们填写的是百度的首页,然后在响应成功的回调里打印出服务器返回的内容,在响应失败的回调里打印出失败的详细信息。

最后,将这个StringRequest对象添加到RequestQueue里面就可以了,如下代码:

mQueue.add(stringRequest);

由于Volley是要访问网络,因此需要在AndroidManifest.xml中添加如下权限:

<uses-permission android:name="android.permission.INTERNET" />

现在运行程序,发出这样一条HTTP请求,就会看到LogCat中会打印出如下图所示的数据:
这里写图片描述

一个最基本的HTTP发送与响应的功能就完成了。你会发现根本还没写几行代码就轻易实现了这个功能,主要就是进行了以下三步操作:

  • 创建一个RequestQueue对象
  • 创建一个StringRequest对象
  • 将StringRequest对象添加到RequestQueue里面

HTTP的请求类型通常有两种,GET和POST,刚才我们使用的是一个GET请求,那么如果想要发出一条POST请求应该怎么做呢?StringRequest中还提供了另外一种四个参数的构造函数,其中第一个参数就是指定请求类型的,我们可以使用如下方式进行指定:

StringRequest stringRequest = new StringRequest(Method.POST, url,  listener, errorListener);

可是这只是指定了HTTP请求方式是POST,那么如何设置提交给服务器的参数呢?很遗憾,StringRequest中并没有提供设置POST参数的方法,但是当发出POST请求的时候,Volley会尝试调用StringRequest的父类——Request中的getParams()方法来获取POST参数,那么我们只需要在StringRequest的匿名类中重写getParams()方法,在这里设置POST参数就可以了,代码如下所示:

StringRequest stringRequest = new StringRequest(Method.POST, "http://www.baidu.com", 
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        Log.d(TAG, response);
                    }
                }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Log.e(TAG, error.getMessage(), error); 
                    }
                }) {
            @Override  
            protected Map<String, String> getParams() throws AuthFailureError {
                Map<String, String> map = new HashMap<String, String>();
                map.put("params1", "value1");
                map.put("params2", "value2");
                return map;
            }
        };

JsonRequest的用法

类似于StringRequest,JsonRequest也是继承自Request类的,不过由于JsonRequest是一个抽象类,因此我们无法直接创建它的实例,那么只能从它的子类入手了。JsonRequest有两个直接的子类,JsonObjectRequest和JsonArrayRequest,一个是用于请求一段JSON数据,一个是用于请求一段JSON数组。

先new出一个JsonObjectRequest对象,如下代码:

JsonObjectRequest jsonObjectRequest = new JsonObjectRequest("http://m.weather.com.cn/data/101010100.html", null,
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject response) {
                        Log.d(TAG, response.toString());
                    }
                }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Log.e(TAG, error.getMessage(), error); 
                    }
                });

URL地址http://m.weather.com.cn/data/101010100.html,是中国天气网提供的一个查询天气信息的接口,响应的数据就是以JSON格式返回的,然后我们在onResponse()方法中将返回的数据打印出来。

最后再将这个JsonObjectRequest对象添加到RequestQueue里就可以了,如下代码:

mQueue.add(jsonObjectRequest);

这样当HTTP通信完成之后,服务器响应的天气信息就会回调到onResponse()方法中,并打印出来。现在运行一下程序,发出这样一条HTTP请求,就会看到LogCat中会打印出如下图所示的数据:
这里写图片描述

可以看出,服务器返回的数据是JSON格式的,并且onResponse()方法中携带的参数也正是一个JSONObject对象,之后只需要从JSONObject对象取出我们想要得到的那部分数据就可以了。

有没有发现,JsonObjectRequest的用法和StringRequest的用法基本上是完全一样的,Volley的易用之处也在这里体现出来了,会了一种就可以让你举一反三,因此关于JsonArrayRequest的用法这里就不讲解了。

ImageRequest的用法

既然Volley是将AsyncHttpClient和Universal-Image-Loader的优点集成于一身的一个框架。我们知道,Universal-Image-Loader具备非常强大的加载网络图片的功能,那么使用Volley,也可以实现基本类似的效果,并且在性能上也豪不逊色于Universal-Image-Loader。

ImageRequest也是继承自Request的,因此它的用法基本与StringRequest、JsonRequest相同:

  • 创建一个RequestQueue对象
  • 创建一个Request对象
  • 将Request对象添加到RequestQueue里面

因此首先需要获取到一个RequestQueue对象,可以调用如下方法获取到:

RequestQueue mQueue = Volley.newRequestQueue(context);

然后new一个ImageRequest对象,代码如下:

ImageRequest mImageRequest = new ImageRequest("https://img-my.youkuaiyun.com/uploads/201407/26/1406383264_4787.jpg",
                new Response.Listener<Bitmap>() {
                    @Override
                    public void onResponse(Bitmap response) {
                        mImageView.setImageBitmap(response);
                    }
                }, 0, 0, Config.RGB_565, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Toast.makeText(getApplicationContext(), "加載图片失败!", Toast.LENGTH_SHORT).show();
                        mImageView.setImageResource(R.drawable.url_image_loading);
                    }
                });

可以看到,ImageRequest的构造函数接收6个参数:

  • 参数1:图片的URL地址
  • 参数2:图片请求成功的回调
  • 参数3:指定允许图片最大的宽度
  • 参数4:指定允许图片最大的高度(如果指定的网络图片的宽度或高度大于这里的最大值,则会对图片进行压缩,指定成0的话就表示不管图片有多大,都不会进行压缩)
  • 参数5:指定图片的颜色属性(Bitmap.Config下的几个常量都可以在这里使用,其中ARGB_8888可以展示最好的颜色属性,每个图片像素占据4个字节的大小,而RGB_565则表示每个图片像素占据2个字节大小)
  • 参数6:图片请求失败的回调

最后将这个ImageRequest对象添加到RequestQueue里,如下代码:

mQueue.add(mImageRequest);

运行程序,并尝试发出这样一条网络请求,就能看到网络上的图片在ImageView中显示出来了,如下图所示:
这里写图片描述

ImageLoader的用法

ImageLoader也可以用于加载网络上的图片,并且它的内部也是使用ImageRequest来实现的,不过ImageLoader明显要比ImageRequest更加高效,因为它不仅可以对图片进行缓存,还可以过滤掉重复的链接,避免重复发送请求。

由于ImageLoader已经不是继承自Request的了,所以它的用法也和我们之前学到的内容有所不同,总结起来大致可以分为以下四步:

  1. 创建一个RequestQueue对象
  2. 创建一个ImageLoader对象
  3. 获取一个ImageListener对象
  4. 调用ImageLoader的get()方法加载网络上的图片

下面我们就来按照这个步骤,学习一下ImageLoader的用法。

第一步:创建一个RequestQueue对象,如下代码:

RequestQueue mQueue = Volley.newRequestQueue(context);

第二步:创建一个ImageLoader对象,如下代码:

ImageLoader mImageLoader = new ImageLoader(mQueue, new ImageCache() {
            @Override
            public void putBitmap(String url, Bitmap bitmap) {

            }

            @Override
            public Bitmap getBitmap(String url) {
                return null;
            }
        });

可以看到,ImageLoader的构造函数接收两个参数:

  • 参数1:RequestQueue对象
  • 参数2:ImageCache对象(这里我们先new出一个空的ImageCache的实现即可)

第三步:获取一个ImageListener对象,如下代码:

ImageListener listener = ImageLoader.getImageListener(mImageView, R.drawable.url_image_loading, R.drawable.url_image_failed);

通过调用ImageLoader的getImageListener()方法能够获取到一个ImageListener对象,getImageListener()方法接收三个参数:

  • 参数1:指定用于显示图片的ImageView控件
  • 参数2:指定加载图片的过程中显示的图片
  • 参数3:指定加载图片失败的情况下显示的图片

第四步,调用ImageLoader的get()方法来加载图片,代码如下:

mImageLoader.get("https://img-my.youkuaiyun.com/uploads/201407/26/1406383264_4787.jpg", listener);

get()方法接收两个参数:
- 参数1:是图片的URL地址
- 参数2:获取到的ImageListener对象

当然,如果想对图片的大小进行限制,也可以使用get()方法的重载,指定图片允许的最大宽度和高度,如下代码:

imageLoader.get("https://img-my.youkuaiyun.com/uploads/201407/26/1406383264_4787.jpg", listener, 200, 200)

运行程序并开始加载图片,可以看到ImageView中会先显示一张默认的图片,等到网络上的图片加载完成后,ImageView则会自动显示该图,如下图所示:
这里写图片描述

虽然现在已经掌握了ImageLoader的用法,但刚才介绍的ImageLoader的优点却还没有使用到。因为这里创建的ImageCache对象是一个空的实现,完全没能起到图片缓存的作用。其实写一个ImageCache也非常简单,但是如果想要写一个性能非常好的ImageCache,最好借助Android提供的LruCache功能。

新建一个BitmapCache并实现ImageCache接口,如下代码:

public class BitmapCache implements ImageCache {

    private LruCache<String, Bitmap> mCache;

    public BitmapCache() {
        int maxSize = 10 * 1024 * 1024; // 缓存图片的大小设置为10M
        mCache = new LruCache<String, Bitmap>(maxSize) {  
            @Override  
            protected int sizeOf(String key, Bitmap bitmap) {  
                return bitmap.getRowBytes() * bitmap.getHeight();  
            }  
        };
    }

    @Override
    public Bitmap getBitmap(String url) {
         return mCache.get(url); 
    }

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

}

这里将缓存图片的大小设置为10M。接着修改创建ImageLoader实例的代码,第二个参数传入BitmapCache的实例,如下代码:

ImageLoader mImageLoader = new ImageLoader(mQueue, new BitmapCache());

NetworkImageView的用法

Volley还提供了NetworkImageView方式加载网络图片,NetworkImageView是一个自定义控制,它是继承自ImageView的,具备ImageView控件的所有功能,并且在原生的基础之上加入了加载网络图片的功能。NetworkImageView控件的用法要比前两种方式更加简单,大致可以分为以下五步:

  1. 创建一个RequestQueue对象
  2. 创建一个ImageLoader对象
  3. 在布局文件中添加一个NetworkImageView控件
  4. 在代码中获取该控件的实例
  5. 设置要加载的图片地址

其中,第一、二步和ImageLoader的用法是完全一样的,因此这里直接从第三步开始学习。首先修改布局文件中的代码,在里面加入NetworkImageView控件,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ScrollView"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:scrollbars="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <com.android.volley.toolbox.NetworkImageView
            android:id="@+id/network_image_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:contentDescription="@null" />
    </LinearLayout>

</ScrollView>

然后在Activity中获取这个控件的实例,代码如下:

mNetworkImageView = (NetworkImageView) findViewById(R.id.network_image_view);

得到了NetworkImageView控件的实例之后,我们可以调用它的setDefaultImageResId()、setErrorImageResId()和setImageUrl()分别设置加载中显示的图片,加载失败时显示的图片,以及目标图片的URL地址,如下代码:

mNetworkImageView.setDefaultImageResId(R.drawable.url_image_loading);
mNetworkImageView.setErrorImageResId(R.drawable.url_image_failed);
mNetworkImageView.setImageUrl("https://img-my.youkuaiyun.com/uploads/201407/26/1406383264_4787.jpg", mImageLoader);

其中,setImageUrl()方法接收两个参数

  • 参数1:指定图片的URL地址
  • 参数2:建好的ImageLoader对象

运行程序,将看到和使用ImageLoader来加载图片一模一样的效果。

NetworkImageView不需要提供任何设置最大宽高的方法也能够对加载的图片进行压缩。这是由于NetworkImageView是一个控件,在加载图片的时候它会自动获取自身的宽高,然后对比网络图片的宽度,再决定是否需要对图片进行压缩。也就是说,压缩过程是在内部完全自动化的,并不需要我们关心,NetworkImageView会始终呈现给我们一张大小刚刚好的网络图片,不会多占用任何一点内存,这也是NetworkImageView最简单好用的一点。

如果不想对图片进行压缩,只需要在布局文件中把NetworkImageView的layout_width和layout_height都设置成wrap_content就可以了,这样NetworkImageView就会将该图片的原始大小展示出来,不会进行任何压缩。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值