Volley的使用以及源码解析

来源:互联网 发布:mac喵呜字体下载 编辑:程序博客网 时间:2024/05/16 01:45

1. 功能介绍

1.1. Volley
Volley 是 Google 推出的 Android 异步网络请求框架和图片加载框架。在 Google I/O 2013 大会上发布。
谷歌大会配图
从名字由来和配图中无数急促的火箭可以看出 Volley 的特点:特别适合数据量小,通信频繁的网络操作。(个人认为 Android 应用中绝大多数的网络操作都属于这种类型)。

1.2 Volley 的主要特点

(1). 扩展性强。Volley 中大多是基于接口的设计,可配置性强。
(2). 一定程度符合 Http 规范,包括返回 ResponseCode(2xx、3xx、4xx、5xx)的处理,请求头的处理,缓存机制的支持等。并支持重试及优先级定义。
(3). 默认 Android2.3 及以上基于 HttpURLConnection,2.3 以下基于 HttpClient 实现,这两者的区别及优劣在4.2.1 Volley中具体介绍。
(4). 提供简便的图片加载工具。

1.3 Google公司为什么会去搞一个volley框架?
1.用户开启一个activity,然后加载网络,这个时候.如果用户点击了finish按钮.activity被销毁了–>网络请求和activity的生命周期是应该联动起来的.
2.listview加载图片的情况比较多.如果用户快速的去滑动listview–>getView->快速的加载图片,用户停止操作的时候.其实真正现实的图片最多就几张—>图片应该缓存起来(内存 +本地 )
3.如果用户打开了一个activity,用户旋转了一下屏幕.activity会旋转–>生命周期重走了–>网络请求缓存
4.之前我们的网络请求,httpurlconnection,httpclient,asynctask(api)–>android sdk–>封装性不够好.1000个开发者就有1000种使用方式–>不够统一
5.理念很容易理解,是开源的.

2. 总体设计

2.1 总体设计图
这里写图片描述

上面是 Volley 的总体设计图,主要是通过两种Dispatch Thread不断从RequestQueue中取出请求,根据是否已缓存调用Cache或Network这两类数据获取接口之一,从内存缓存或是服务器取得请求的数据,然后交由ResponseDelivery去做结果分发及回调处理。

2.2 Volley 中的概念
Volley 的调用比较简单,通过 newRequestQueue(…) 函数新建并启动一个请求队列RequestQueue后,只需要往这个RequestQueue不断 add Request 即可。
Volley:Volley 对外暴露的 API,通过 newRequestQueue(…) 函数新建并启动一个请求队列RequestQueue。
Request:表示一个请求的抽象类。StringRequest、JsonRequest、ImageRequest 都是它的子类,表示某种类型的请求。
RequestQueue:表示请求队列,里面包含一个CacheDispatcher(用于处理走缓存请求的调度线程)、NetworkDispatcher数组(用于处理走网络请求的调度线程),一个ResponseDelivery(返回结果分发接口),通过 start() 函数启动时会启动CacheDispatcher和NetworkDispatchers。
CacheDispatcher:一个线程,用于调度处理走缓存的请求。启动后会不断从缓存请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给ResponseDelivery去执行后续处理。当结果未缓存过、缓存失效或缓存需要刷新的情况下,该请求都需要重新进入NetworkDispatcher去调度处理。
NetworkDispatcher:一个线程,用于调度处理走网络的请求。启动后会不断从网络请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给ResponseDelivery去执行后续处理,并判断结果是否要进行缓存。
ResponseDelivery:返回结果分发接口,目前只有基于ExecutorDelivery的在入参 handler 对应线程内进行分发。
HttpStack:处理 Http 请求,返回请求结果。目前 Volley 中有基于 HttpURLConnection 的HurlStack和 基于 Apache HttpClient 的HttpClientStack。
Network:调用HttpStack处理请求,并将结果转换为可被ResponseDelivery处理的NetworkResponse。
Cache:缓存请求结果,Volley 默认使用的是基于 sdcard 的DiskBasedCache。NetworkDispatcher得到请求结果后判断是否需要存储在 Cache,CacheDispatcher会从 Cache 中取缓存结果。

3. 流程图

Volley 请求流程图
这里写图片描述

4. 详细设计

4.1 类关系图
这里写图片描述
这是 Volley 框架的主要类关系图
图中红色圈内的部分,组成了 Volley 框架的核心,围绕 RequestQueue 类,将各个功能点以组合的方式结合在了一起。各个功能点也都是以接口或者抽象类的形式提供。
红色圈外面的部分,在 Volley 源码中放在了 toolbox 包中,作为 Volley 为各个功能点提供的默认的具体实现。
通过类图我们看出, Volley 有着非常好的拓展性。通过各个功能点的接口,我们可以给出自定义的,更符合我们需求的具体实现。
多用组合,少用继承;针对接口编程,不针对具体实现编程。
优秀框架的设计,令人叫绝,受益良多。

5.Volley两个核心类

  • Request:一个请求
    • StringRequest:请求的时候直接回来一个String 文本信息 —>url–>jsonString
    • JsonObjectRequest:请求的时候直接回来一个JsonObject —> url–>JsonObject
    • JsonArrayRequest:请求的时候直接回来一个JsonArray —> url–>JsonArray
    • ImageRequest:请求的时候直接回来一个Bitmap
    • 自定义请求:一会我们会结合gson 自定义请求 GsonRequest–>Bean
  • ImageLoader:图片的加载器
  • NetWorkImageView:继承了imageView,对ImageView进行了拓展
  • RequestQueue:请求队列.

测试主要代码如下

public class MainActivity extends Activity {    private ImageView           mIv;    private NetworkImageView    mNiv;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mIv = (ImageView) findViewById(R.id.iv);        mNiv = (NetworkImageView) findViewById(R.id.niv);        initStringRequest();        initJsonObjectRequest();// url-->JsonObject-->Bean        initJsonArrayRequest();        initImageRequest();        // initNetWorkImageView();        initImageLoader();        initGsonRequest();    }    private void initGsonRequest() {        findViewById(R.id.btn6).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                // 1.                String url = "http://188.188.6.100:8080/json/ip.json";                GsonRequest<Ip> gsonRequest = new GsonRequest<Ip>(url, new Response.ErrorListener() {                    @Override                    public void onErrorResponse(VolleyError error) {                        System.out.println("error:" + error.getMessage());                    }                }, Ip.class, new Listener<Ip>() {                    @Override                    public void onResponse(Ip ip) {                        System.out.println(ip.origin);                    }                });                /*// 2.                RequestQueue queue = Volley.newRequestQueue(MainActivity.this);                // 3.                queue.add(gsonRequest);*/                VolleyTools.getInstance(MainActivity.this).getQueue().add(gsonRequest);            }        });    }    private void initImageLoader() {        findViewById(R.id.btn5).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                RequestQueue queue = Volley.newRequestQueue(MainActivity.this);                ImageCache imageCache = new LruMemImageCache();                ImageLoader imageLoader = new ImageLoader(queue, imageCache);                String requestUrl = "http://188.188.6.100:8080/img/3812b31bb051f81901e3b44dd8b44aed2f73e7f7.jpg";                imageLoader.get(requestUrl, new ImageListener() {                    @Override                    public void onErrorResponse(VolleyError error) {                        System.out.println("error:" + error.getMessage());                    }                    @Override                    public void onResponse(ImageContainer response, boolean isImmediate) {                        Bitmap bitmap = response.getBitmap();                        mIv.setImageBitmap(bitmap);                    }                }, 0, 0);            }        });    }    private void initNetWorkImageView() {        mNiv.setDefaultImageResId(R.drawable.ic_launcher);        mNiv.setErrorImageResId(R.drawable.ic_launcher_error);        String url = "http://188.188.6.100:8080/img/3812b31bb051f81901e3b44dd8b44aed2f73e7f7.jpg";        RequestQueue queue = Volley.newRequestQueue(MainActivity.this);        /**         * volley里面没有内存以及磁盘缓存,只是提供了图片缓存的核心操作(存,取)         */        ImageCache imageCache = new LruMemImageCache();        ImageLoader imageLoader = new ImageLoader(queue, imageCache);        // ImageLoader imageLoader = new ImageLoader(请求对象, imageCache接口对象);        mNiv.setImageUrl(url, imageLoader);    }    private void initImageRequest() {        findViewById(R.id.btn4).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                /**                 maxWidth Maximum:是0,就是原样输出,非0,就是按照指定的像素值输出了吧                 maxHeight Maximum :是0,就是原样输出,非0,就是按照指定的像素值输出了吧                  帮我们进行了大图片的处理                 */                // 1.                String url = "http://188.188.6.100:8080/img/3812b31bb051f81901e3b44dd8b44aed2f73e7f7.jpg";                ImageRequest imageRequest = new ImageRequest(url, new Listener<Bitmap>() {                    @Override                    public void onResponse(Bitmap bitmap) {                        mIv.setImageBitmap(bitmap);                    }                }, 200, 100, Config.ARGB_4444, new Response.ErrorListener() {                    @Override                    public void onErrorResponse(VolleyError error) {                        System.out.println("error:" + error.getMessage());                    }                });                // 2.                RequestQueue queue = Volley.newRequestQueue(MainActivity.this);                // 3.                queue.add(imageRequest);            }        });    }    private void initJsonArrayRequest() {        findViewById(R.id.btn3).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                // 1.创建请求对象                String url = "http://188.188.6.100:8080/json/jsonArray.json";                JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(url, new Listener<JSONArray>() {                    @Override                    public void onResponse(JSONArray response) {                        System.out.println("success:" + response.length());                    }                }, new Response.ErrorListener() {                    @Override                    public void onErrorResponse(VolleyError error) {                        System.out.println("error:" + error.getMessage());                    }                });                // 2.创建队列                RequestQueue queue = Volley.newRequestQueue(MainActivity.this);                // 3.发起请求                queue.add(jsonArrayRequest);            }        });    }    private void initJsonObjectRequest() {        findViewById(R.id.btn2).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                // 1. 创建相应的请求对象                String url = "http://188.188.6.100:8080/json/ip.json";                JSONObject jsonRequest = null;// 请求参数可以通过jsonObject传递                JsonObjectRequest jsonObjectRequest =                        new JsonObjectRequest(url, jsonRequest, new Listener<JSONObject>() {                            @Override                            public void onResponse(JSONObject response) {                                // jsonString-->JSONObject                                System.out.println("success:" + response.optString("origin"));                            }                        }, new ErrorListener() {                            @Override                            public void onErrorResponse(VolleyError error) {                                System.out.println("error:" + error.getMessage());                            }                        });                // 2.创建请求队列                RequestQueue queue = Volley.newRequestQueue(MainActivity.this);                // 3.请求对象放入到请求队列中                queue.add(jsonObjectRequest);            }        });    }    private void initStringRequest() {        findViewById(R.id.btn1).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                String url = "http://www.baidu.com";                // 1.创建相应的 请求对象                StringRequest stringRequest = new StringRequest(url, new Listener<String>() {                    @Override                    public void onResponse(String response) {                        System.out.println("success:" + response);                    }                }, new Response.ErrorListener() {                    @Override                    public void onErrorResponse(VolleyError error) {                        System.out.println("error:" + error.getMessage());                    }                });                // 2.创建 请求队列                RequestQueue queue = Volley.newRequestQueue(MainActivity.this);                // 3.发起请求 请求对象,放到请求队列                queue.add(stringRequest);            }        });    }}

布局文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context=".MainActivity" >    <Button        android:id="@+id/btn1"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="StringRequest" />    <Button        android:id="@+id/btn2"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="JsonObjectRequest" />    <Button        android:id="@+id/btn3"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="JsonArrayRequest" />    <Button        android:id="@+id/btn4"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="ImageRequest" />    <Button        android:id="@+id/btn5"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="ImageLoader" />    <Button        android:id="@+id/btn6"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="GsonRequest" />    <ImageView        android:id="@+id/iv"        android:layout_width="wrap_content"        android:layout_height="wrap_content" />    <com.android.volley.toolbox.NetworkImageView        android:id="@+id/niv"        android:layout_width="wrap_content"        android:layout_height="wrap_content" /></LinearLayout>

自定义GsonRequest请求成功之后返回JavaBean

public class GsonRequest<T> extends Request<T> {    private Class<T>    clazz;    private Listener<T> mListener;    public GsonRequest(String url, ErrorListener errorListener, Class<T> clazz, Listener<T> listener) {        super(url, errorListener);        this.clazz = clazz;        this.mListener = listener;    }    /**     * 解析响应的数据     */    @Override    protected Response<T> parseNetworkResponse(NetworkResponse response) {        String jsonString;        try {            jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers));        } catch (UnsupportedEncodingException e) {            jsonString = new String(response.data);        }        // jsonString-->Bean        T t;        try {            Gson gson = new Gson();            t = gson.fromJson(jsonString, clazz);            return Response.success(t, HttpHeaderParser.parseCacheHeaders(response));        } catch (JsonSyntaxException e) {            e.printStackTrace();            return Response.error(new ParseError());        }    }    /**     * 传递结果     */    @Override    protected void deliverResponse(T response) {        mListener.onResponse(response);    }}

由于Volley没有实现内存缓存与磁盘缓存只提供了接口,需要我们自己实现(内存缓存代码如下<根据LruCache缓存策略>)

public class LruMemImageCache implements ImageCache {    private LruCache<String, Bitmap>    mLruCache;    // 1.定义内存缓存的最大值    // private int maxSize = 4; // 内存缓存的最大值 4m    private int                         maxSize = 4 * 1024 * 1024;  // 内存缓存的最大值 4194304 byte    public LruMemImageCache() {        super();        mLruCache = new LruCache<String, Bitmap>(maxSize) {            // 2.覆写sizeOf方法            /**             * sizeOf方法是做啥的?             *   1.返回的是放到LinkedHashMap中具体实体的大小             *   2.统一和maxSize定义的单位             */            @Override            protected int sizeOf(String key, Bitmap bitmap) {                // return bitmap.getByteCount() / 1024 / 1024;//每个图片是多少m                return bitmap.getByteCount();// byte//每个图片的是多少byte                // return super.sizeOf(key, value);            }        };    }    /**     * 取的操作     */    @Override    public Bitmap getBitmap(String url) {        Bitmap bitmap = mLruCache.get(url);        if (bitmap != null) {            return bitmap;        }        return null;    }    /**     * 存的操作     */    @Override    public void putBitmap(String url, Bitmap bitmap) {        mLruCache.put(url, bitmap);    }}

磁盘缓存没有写,可以直接参考郭霖与张鸿洋博客,里面分析了LruCache与DiskLruCache的使用与源码

博客地址如下: http://blog.csdn.net/lmj623565791/article/details/47251585

http://blog.csdn.net/guolin_blog/article/details/28863651

http://blog.csdn.net/guolin_blog/article/details/34093441

VolleyTools只有一个实例,其实是mImageLoader,mLruMemImageCache,mQueue只需要初始化一次即可(单例)
public class VolleyTools {

ImageLoader         mImageLoader;LruMemImageCache    mLruMemImageCache;RequestQueue        mQueue;public ImageLoader getImageLoader() {    return mImageLoader;}public LruMemImageCache getLruMemImageCache() {    return mLruMemImageCache;}public RequestQueue getQueue() {    return mQueue;}private static VolleyTools  instance;private VolleyTools(Context context) {    mQueue = Volley.newRequestQueue(context);    mLruMemImageCache = new LruMemImageCache();    mImageLoader = new ImageLoader(mQueue, mLruMemImageCache);}public static VolleyTools getInstance(Context context) {    if (instance == null) {        synchronized (VolleyTools.class) {            if (instance == null) {                instance = new VolleyTools(context);            }        }    }    return instance;}

}

0 0