Volley主页https://android.googlesource.com/platform/frameworks/volley

http://www.youtube.com/watch?v=yhv8l9F44qo&feature=player_embedded

1. 什么是Volley

Google I/O 2013上,Volley发布了volley。在这之前,我们在程序中需要和网络通信的时候,大体使用的东西莫过于

  • AsyncTaskLoader
  • HttpURLConnection
  • AsyncTask
  • HTTPClient(Apache)等

Volley是Android平台上的网络通信库,能使网络通信更快,更简单,更健壮。

Volley名称的由来: a burst or emission of many things or a large amount at once

在Google IO的演讲上,其配图是一幅发射火弓箭的图,有点类似流星。见下图

其实,从这幅图,我们也可以看出来,Volley特别适合数据量不大但是通信频繁的场景。volley适合快速,简单的请求(Json对象,图片加载)。

Volley引入的背景

在以前,我们可能面临如下很多麻烦的问题。比如以前从网上下载图片的步骤可能是这样的流程:

  • 在ListAdapter#getView()里开始图像的读取。
  • 通过AsyncTask等机制使用HttpURLConnection从服务器去的图片资源
  • 在AsyncTask#onPostExecute()里设置相应ImageView的属性。

而在Volley下,只需要一个函数即可,详细见后面的例子。

再比如,屏幕旋转的时候,有时候会导致再次从网络取得数据。为了避免这种不必要的网络访问,我们可能需要自己写很多针对各种情况的处理,比如cache什么的。

再有,比如ListView的时候,我们滚动过快,可能导致有些网络请求返回的时候,早已经滚过了当时的位置,根本没必要显示在list里了,虽然我们可以通过ViewHolder来保持url等来实现防止两次取得,但是那些已经没有必须要的数据,还是会浪费系统的各种资源。

Volley提供的功能

简单来说,它提供了如下的便利功能:

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

使用前的准备

引入Volley非常简单,首先,从git库先克隆一个下来:

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

然后编译为jar包,再在自己的工程里import进来。

注意,这个库要求最低SDK版本为Froyo,即至少要设置android:minSdkVersion为8以上。

使用例子

最简单的get请求

这个例子很简单,从网络取得JSON对象,然后打印出来。

    mQueue = Volley.newRequestQueue(getApplicationContext());      mQueue.add(new JsonObjectRequest(Method.GET, url, null,                  new Listener() {                      @Override                      public void onResponse(JSONObject response) {                          Log.d(TAG, "response : " + response.toString());                      }                  }, null));      mQueue.start();  


3.2. 给ImageView设置图片源
    // imageView是一个ImageView实例      // ImageLoader.getImageListener的第二个参数是默认的图片resource id      // 第三个参数是请求失败时候的资源id,可以指定为0      ImageListener listener = ImageLoader.getImageListener(imageView, android.R.drawable.ic_menu_rotate, android.R.drawable.ic_delete);      mImageLoader.get(url, listener);  
ImageLoader的方法都需要从主线程里来调用。

使用NetworkImageView

Volley提供了一个新的控件NetworkImageView来代替传统的ImageView,这个控件的图片属性可以通过

mImageView.setImageUrl(url, imageLoader)  
来设定。而且,这个控件在被从父控件detach的时候,会自动取消网络请求的,即完全不用我们担心相关网络请求的生命周期问题。

示例代码如下:
mImageLoader = new ImageLoader(mRequestQueue, new BitmapLruCache());  ... ...     if(holder.imageRequest != null) {      holder.imageRequest.cancel();  }  holder.imageRequest = mImageLoader.get(BASE_UR + item.image_url, holder.imageView, R.drawable.loading, R.drawable.error); 

注意,这里使用的不是ImageView控件,而是Volley新提供的com.android.volley.NetworkImageView。

另外,注意这里:

mImageLoader = new ImageLoader(mRequestQueue, new BitmapLruCache());  <span style="color:#000000;"><a target=_blank target="_blank" href="http://blog.csdn.net/t12x3456/article/details/9221611#" class="ViewSource" title="view plain" style="text-decoration: none; background-color: inherit; border: medium none; padding: 1px; margin: 0px 10px 0px 0px; font-size: 9px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px;">ew plain</a></span>


ImageLoader构造函数的第二个参数是一个ImageCache的实例(严格来说,是实现ImageCache接口的某具体类的实例)
ImageCache的定义如下(在ImageLoader.java里):
    /**      * Simple cache adapter interface. If provided to the ImageLoader, it      * will be used as an L1 cache before dispatch to Volley. Implementations      * must not block. Implementation with an LruCache is recommended.      */      public interface ImageCache {          public Bitmap getBitmap(String url);          public void putBitmap(String url, Bitmap bitmap);      }  


下面的网址一个lru的cache实现例子,请参考:

https://github.com/suwa-yuki/VolleySample/blob/master/src/jp/classmethod/android/sample/volley/BitmapCache.java

使用自己定制的request

我们也可以通过继承Request根据自己的需求来定制自己的request

    @Override      protected Response parseNetworkResponse(NetworkResponse response) {          try {              String json = new String(                      response.data, HttpHeaderParser.parseCharset(response.headers));              return Response.success(                      gson.fromJson(json, clazz), HttpHeaderParser.parseCacheHeaders(response));          } catch (UnsupportedEncodingException e) {              return Response.error(new ParseError(e));          } catch (JsonSyntaxException e) {              return Response.error(new ParseError(e));          }      }  

这段代码节选自: https://gist.github.com/ficusk/5474673

里面使用的gson(com.google.gson.Gson)是JSON的序列化和反序列化的库,可以在JSON和java model object之间进行转换。

以下是使用自定制request的例子:

    mRequestQueue.add( new GsonRequest(url, ListResponse.class, null,          new Listener() {              public void onResponse(ListResponse response) {                  appendItemsToList(response.item);                  notifyDataSetChanged();              }          }      }  

Volley的架构设计

Volley使用了线程池来作为基础结构,主要分为主线程,cache线程和network线程。

主线程和cache线程都只有一个,而NetworkDispatcher线程可以有多个,这样能解决比并行问题。如下图:


其中蓝色部分代表主线程,绿色部分代表缓存线程,橙色部分代表网络线程。我们在主线程中调用RequestQueue的add()方法来添加一条网络请求,这条请求会先被加入到缓存队列当中,如果发现可以找到相应的缓存结果就直接读取缓存并解析,然后回调给主线程。如果在缓存中没有找到结果,则将这条请求加入到网络请求队列中,然后处理发送HTTP请求,解析响应结果,写入缓存,并回调主线程。

如果在一个Activity里面启动了网络请求,而在这个网络请求还没返回结果的时候,如果Activity被结束了,则我们需要写如下代码作为防守:

@Override public void onPostExecute(Result r) {      if (getActivity() == null) {          return;      }      // ...  } 

Activity被终止之后,如果继续使用其中的Context等,除了无辜的浪费CPU,电池,网络等资源,有可能还会导致程序crash,所以,我们需要处理这种一场情况。

使用Volley的话,我们可以在Activity停止的时候,同时取消所有或部分未完成的网络请求。

Volley里所有的请求结果会返回给主进程,如果在主进程里取消了某些请求,则这些请求将不会被返回给主线程。
比如,可以针对某些个request做取消操作y

    @Override      public void onStop() {          for (Request <?> req : mInFlightRequests) {              req.cancel();          }          ...      }  

或者,取消这个队列里的所有请求:

    @Override pubic void onStop() {          mRequestQueue.cancelAll(this);          ...      }  


也可以根据RequestFilter或者Tag来终止某些请求:

    @Override public void onStop() {          mRequestQueue.cancelAll( new RequestFilter() {})          ...          // or          mRequestQueue.cancelAll(new Object());          ...  


总结

从演讲的例子来看,Volley应该是简化了网络通信的一些开发,特别是针对如下两种情况:

  • JSON对象
  • 图片加载

但是这个东西也有不实用的地方,比如大数据(large payloads ),流媒体,这些case,还需要使用原始的方法,比如Download Manager等。


案例分析

接下来,我们来学习简单的使用下volley给我提供的API吧。

首先拿到一个请求队列

(RequestQueue只需要一个实例即可,不像AsyncTask每次使用都要new一个)

    // 初始化RequestQueue一个activity只需要一个          private void initRequestQueue() {              mQueue = Volley.newRequestQueue(getApplicationContext());          }  


实现volley的异步请求类

(JsonObjectRequest,JsonArrayRequest,StringRequest,ImageRequest)

由于用法都相差不大,我就不一一举例了,举几个常用有代表性的例子:

StringRequest的get请求
    private void loadGetStr(String url) {                StringRequest srReq = new StringRequest(Request.Method.GET, url,                  new StrListener(), new StrErrListener()) {                    protected final String TYPE_UTF8_CHARSET = "charset=UTF-8";                    // 重写parseNetworkResponse方法改变返回头参数解决乱码问题              // 主要是看服务器编码,如果服务器编码不是UTF-8的话那么就需要自己转换,反之则不需要              @Override              protected Response<String> parseNetworkResponse(                      NetworkResponse response) {                  try {                      String type = response.headers.get(HTTP.CONTENT_TYPE);                      if (type == null) {                          type = TYPE_UTF8_CHARSET;                          response.headers.put(HTTP.CONTENT_TYPE, type);                      } else if (!type.contains("UTF-8")) {                          type += ";" + TYPE_UTF8_CHARSET;                          response.headers.put(HTTP.CONTENT_TYPE, type);                      }                  } catch (Exception e) {                  }                  return super.parseNetworkResponse(response);              }          };          srReq.setShouldCache(true); // 控制是否缓存          startVolley(srReq);      }  


JsonObjectRequest的post请求:

    // post请求      private void loadPostJson(String url) {          // 第二个参数说明:          // Constructor which defaults to GET if jsonRequest is null, POST          // otherwise.          // 默认情况下设成null为get方法,否则为post方法。          JsonObjectRequest srReq = new JsonObjectRequest(url, null,                  new JsonListener(), new StrErrListener()) {                    @Override              protected Map<String, String> getParams() throws AuthFailureError {                  Map<String, String> map = new HashMap<String, String>();                  map.put("w", "2459115");                  map.put("u", "f");                  return map;              }          };          srReq.setShouldCache(false); // 控制是否缓存          startVolley(srReq);      }  


大家注意看的话,无论是 JsonObjectReques的postt还是StringRequest的get都需要传入两个监听函数一个是成功一个是失败,成功监听他们会返回相应类型的数据:
    // Str请求成功回调      private class StrListener implements Listener<String> {                @Override          public void onResponse(String arg0) {              Log.e(Tag, arg0);                }            }            // Gson请求成功回调      private class GsonListener implements Listener<ErrorRsp> {                @Override          public void onResponse(ErrorRsp arg0) {              Toast.makeText(mContext, arg0.toString(), Toast.LENGTH_LONG).show();          }            }      // 共用失败回调      private class StrErrListener implements ErrorListener {                @Override          public void onErrorResponse(VolleyError arg0) {              Toast.makeText(mContext,                      VolleyErrorHelper.getMessage(arg0, mContext),                      Toast.LENGTH_LONG).show();          }            }  

ImageRequest
    /**      * 第三第四个参数分别用于指定允许图片最大的宽度和高度,如果指定的网络图片的宽度或高度大于这里的最大值,则会对图片进行压缩,      * 指定成0的话就表示不管图片有多大,都不会进行压缩。      *       * @param url      *            图片地址      * @param listener      * @param maxWidth      *            指定允许图片最大的宽度      * @param maxHeight      *            指定允许图片最大的高度      * @param decodeConfig      *            指定图片的颜色属性,Bitmap.Config下的几个常量.      * @param errorListener      */      private void getImageRequest(final ImageView iv, String url) {          ImageRequest imReq = new ImageRequest(url, new Listener<Bitmap>() {                    @Override              public void onResponse(Bitmap arg0) {                  iv.setImageBitmap(arg0);              }          }, 60, 60, Bitmap.Config.ARGB_8888, new StrErrListener());          startVolley(imReq);      }  

看到现在大家肯定会疑惑写了这么多不同类型的Request到底如何运行?接下请看:
    // 添加及开始请求      private void startVolley(Request req) {                // 设置超时时间          // req.setRetryPolicy(new DefaultRetryPolicy(20 * 1000, 1, 1.0f));          // 将请求加入队列          mQueue.add(req);          // 开始发起请求          mQueue.start();      }  


volley不仅提供了这些请求的方式,还提供了加载图片的一些方法和控件:

比如我们一个列表需要加载很多图片我们可以使用volley给我们提供的ImageLoader(ImageLoader比ImageRequest更加高效,因为它不仅对图片进行缓存,还可以过滤掉重复的链接,避免重复发送请求。)

    public class ImageAdapter extends ArrayAdapter<String> {                    private RequestQueue mQueue;          private ImageLoader mImageLoader;                public ImageAdapter(Context context, List<String> objects) {              super(context, 0, objects);              mQueue = Volley.newRequestQueue(getContext());              mImageLoader = new ImageLoader(mQueue, new BitmapCache());          }                    @Override          public View getView(int position, View convertView, ViewGroup parent) {              String url = getItem(position);              ImageView imageView;              if (convertView == null) {                  imageView = new ImageView(getContext());              } else {                  imageView = (ImageView) convertView;              }              // getImageListener(imageView控件对象,默认图片地址,失败图片地址);              ImageListener listener = ImageLoader.getImageListener(imageView, android.R.drawable.ic_menu_rotate, android.R.drawable.ic_delete);              // get(图片地址,listener,宽,高);自动帮你处理图片的宽高再也不怕大图片的oom了              mImageLoader.get(url, listener,100,200);              return imageView;          }            }  


当然还需要重写ImageCache这个类 //使用LruCache再也不用怕加载多张图片oom了

    public class <span style="font-family: Arial;">BitmapCache</span><span style="font-family: Arial;"> extends LruCache<String, Bitmap> implements ImageCache {</span>          // LruCache 原理:Cache保存一个强引用来限制内容数量,每当Item被访问的时候,此Item就会移动到队列的头部。 当cache已满的时候加入新的item时,在队列尾部的item会被回收。          // 解释:当超出指定内存值则移除最近最少用的图片内存          public static int getDefaultLruCacheSize() {              // 拿到最大内存              final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);              // 拿到内存的八分之一来做图片内存缓存              final int cacheSize = maxMemory / 8;                    return cacheSize;          }                public BitmapLruCache() {              this(getDefaultLruCacheSize());          }                public BitmapLruCache(int sizeInKiloBytes) {              super(sizeInKiloBytes);          }                @Override          protected int sizeOf(String key, Bitmap value) {              return value.getRowBytes() * value.getHeight() / 1024;          }                @Override          public Bitmap getBitmap(String url) {              return get(url);          }                @Override          public void putBitmap(String url, Bitmap bitmap) {              put(url, bitmap);          }      }  

Volley还提供的加载图片的控件 com.android.volley.NetworkImageView。(这个控件在被从父控件detach的时候,会自动取消网络请求的,即完全不用我们担心相关网络请求的生命周期问题,而且NetworkImageView还会根据你对图片设置的width和heigh自动压缩该图片不会产生多的内存,还有NetworkImageView在列表中使用不会图片错误)
<com.android.volley.toolbox.NetworkImageView      android:id="@+id/network_image_view"      android:layout_width="100dp"      android:layout_height="100dp" /> 

使用方法:
    private void networkImageViewUse(NetworkImageView iv, String url) {              ImageLoader imLoader = new ImageLoader(mQueue, new BitmapLruCache());              iv.setDefaultImageResId(R.drawable.ic_launcher);              iv.setErrorImageResId(R.drawable.ic_launcher);              iv.setImageUrl(url, imLoader);          }  



我们说了这么多都是请求,那么如何取消请求呢?

1.activity自动销毁时它会自定取消所有请求。

2.给请求设置标签:

    request.setTag("My Tag");  

取消所有指定标记的请求:

    request.cancelAll("My Tag");    




更多相关文章

  1. Android(安卓)UI学习 - GridView和ImageView的使用
  2. Android(安卓)Universal Image Loader 源码分析(转载)
  3. android 相对布局,代码创建imageview,布局居中问题
  4. Android之---RecycleView实现简单的图片查看功能(类似相册查看器)
  5. Android(安卓)图片转换为 Base64 编码
  6. Android调用系统, 任意比例裁剪图片
  7. Android(安卓)控件之Gallery图片集
  8. Android实现圆角矩形和圆形ImageView的方式
  9. android中异步加载网络图片

随机推荐

  1. Android Fragment使用和切换 笔记
  2. 如何为ListView设置分割线
  3. android 自定义圆形的ImageView
  4. android版本与API对应关系
  5. android SearchView 失去焦点
  6. android DrawerLayout 点击穿透、点击自
  7. Android Gradle Plugin与Gradle 版本对应
  8. gridview第一行与顶端有一定的距离
  9. Android百度地图一种简单实现多标注及响
  10. Android定制之常见问题解决