Volley 解析

Volley

Request处理流程

RequestQueue类中有三个基本的队列。调用RequestQueue.add(request)增加的请求会先增加mCacheQueue(优先级堵塞队列)由CacheDispatcher( 循环读取队列中的请求,当没有请求处理时线程堵塞)线程处理。假设该请求之前已经被缓存。读取缓存返回给主线程结果。否则将请求增加mNetworkQueue由NetworkDispatcher线程处理。

因为处理网络请求比較耗时。NetworkDispatcher线程默认开启四个。

每一个线程都循环从mNetworkQueue读取请求。运行HTTP请求并解析server返回结果,将结果存入缓存,同一时候向通过handler回调到主线程,向用户返回结果。

假设该请求不是第一次处理。在进入mCacheQueue之前,可能回被增加mWaitingRequests(假设有同样url的请求正在处理)。作用是避免反复的请求多次运行,提高请求速度。当请求处理完之后,会检查mWaitingRequests是否有等待的请求。并所有增加缓存队列。

mCacheQueue :PriorityBlockingQueue<Request<?

>>

mNetworkQueue:PriorityBlockingQueue<Request<?>>

mWaitingRequests: Map<String,Queue<Request<?>>

怎样推断缓存是否过期

Expires首部和Cache-Control:max-age首部都是来告诉缓存文档有没有过期。Volley提供的解析类HttpHeaderParser在解析HTTP返回结果时。会从返回头中获取Expires和Cache-Control:max-age相关信息并存储在缓存数据中。假设没有过期还要推断是否须要刷新。一般假设数据没有过期是不须要刷新的。可是假设返回头中包括stale-while-revalidate会将数据提交给用户后请求刷新数据。在实现Request类的抽象方法parseNetworkResponse时,用户必须调用HttpHeaderParser.parseCacheHeaders解析返回头,否则无法推断缓存是否过期。

运行网络请求

假设sdk版本号大于9使用HttpURLConnection运行网络请求,否则使用HttpClient。由BasicNetwork的

performRequest方法运行网络请求。在performRequest会调用Request类的getHeaders获取请求头。

假设server返回的响应头中包括Last-Modified或ETag,在运行下次请求时会在请求头中增加If-None-Match或If-Modified-Since。

假设server上的内容没有改变,会返回304状态。不会返回内容。内容从缓存获取。

自己定义Request

Request类是一个泛型类,当中T用户期望带到的数据类型。Volley已经提供了StringRequest、ImageRequest、JosnObjectRequest等能够将server返回的数据解析为String、Image JSONObject。可是这些Request有时候不能满足用户的需求。

比方假设server返回

Xml格式的数据,server返回的Josn用户不想解析为JosnObject。

能够很方便的对Volley的Request进行扩展。主要是继承Request。重写parseNetworkResponse和deliverResponse方法在parseNetworkResponse中将返回二进制数据转化为须要的数据格式。同一时候调用HttpHeaderParser.parseCacheHeaders得到缓存数据。

在deliverResponse中调用Listenner返回结果。

假设要更改Http的请求头或者在post方法中须要提供数据须要重写getHeaders和getParams方法。

Request实现了Comparable,存储Request使用的是优先级队列。能够对Reqest设定优先级。有LOW,NORMAL,HIGH,IMMEDIATE四中优先级。

默认使用NORMAL。ImageRequest使用的是LOW。

public class GsonRequest<T> extends Request<T> {
private final Listener<T> mListener;
private Gson mGson;
private Class<T> mClass;
public GsonRequest(int method, String url, Class<T> clazz, Listener<T> listener,
        ErrorListener errorListener) {
    super(method, url, errorListener);
    mGson = new Gson();
    mClass = clazz;
    mListener = listener;
}
public GsonRequest(String url, Class<T> clazz, Listener<T> listener,
        ErrorListener errorListener) {
    this(Method.GET, url, clazz, listener, errorListener);
}
@Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
    try {
        String jsonString = new String(response.data,
                HttpHeaderParser.parseCharset(response.headers));
        return Response.success(mGson.fromJson(jsonString, mClass),
                HttpHeaderParser.parseCacheHeaders(response));
    } catch (UnsupportedEncodingException e) {
        return Response.error(new ParseError(e));
    }
}
@Override
protected void deliverResponse(T response) {
    mListener.onResponse(response);
}
}

NetworkImageView 载入图片

NetworkImageView必须配合ImageLoader使用。ImageLoader能够依据url异步载入图片。ImageLoader内部使用ImageRequest载入图片,同一时候对会把同样的请求进行合并降低请求次数。

多个NetworkImageView相应一个ImageLoader。

假设有多个NetworkImageView同一时候请求同一个图片。ImageLoader仅仅会运行一次网络请求。

ImageLoader内部定义了一个接口,该接口的实现由用户提供。该接口用来缓存图片,一般的实现都使用了LRU算法。

public class BitmapLruCache extends LruCache<String, Bitmap> implements ImageLoader.ImageCache {
public static final int ONE_MB = 1048576;
public BitmapLruCache(Context context) {
    super(1048576 * ((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass() / 10);
}
protected int sizeOf(String key, Bitmap value) {
    return Build.VERSION.SDK_INT >= 12?value.getByteCount():value.getRowBytes() * value.getHeight();
}
public Bitmap getBitmap(String url) {
    return (Bitmap)this.get(url);
}
public void putBitmap(String url, Bitmap bitmap) {
    this.put(url, bitmap);
}
}

加上Volley提供的磁盘缓存。NetworkImageView载入图片使用了两级缓存。

  1. 创建ImageLoader

    mLoader = new ImageLoader(Volley.newRequestQueue(context), new BitmapLruCache(context));

  2. 载入图片

    imageView.setImageUrl(girl.getImg(), mLoader);

还能够通过imageView.setDefaultImageResId()来设置图片未载入时显示的默认图片。通过imageView.setErrorImageResId()来设置图片载入失败显示的图片。NetworkImageView会在url地址改变之后会及时取消之前的载入。

Volley 缓存

Volley默认会缓存请求结果。缓存存放在应用的volley文件夹。每一个请求结果相应一个文件。文件名称由url的hash码得到。默认缓存大小为5M。

没有提供更改缓存大小的接口。假设更改大小必须更改代码。缓存採用LUR算法。

内部主要由LinkedHashMap实现。LinkedHashMap内部有一个双向链表和一个HashMap,链表用来保存元素的存储顺序,MAP用来高速存取元素。

private final Map<String, CacheHeader> mEntries =new LinkedHashMap<String, CacheHeader>(16, .75f, true)

必须将LinkedHashMap的第三个參数设为true。LinkedHashMap才会按訪问顺序排序(最少被訪问的entry靠前,近期訪问的entry靠后)。

每次在增加元素时。都会推断缓存大小是否超过5M,假设超过调用pruneIfNeeded将最老的元素移除。缓存文件时,会缓存server返回的http头和body。

Retrofit

Retrofit注解

Retrofit的Annotation包括请求方法相关的@GET、@POST、@HEAD、@PUT、@DELETA、@PATCH。和參数相关的@Path、@Field、@Multipart等。

  • 通过注解指定请求url,方法和參数

    @GET("group/{id}/users")

    Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);

  • 指定请求头

    @Headers("Cache-Control: max-age=640000")

    @GET("widget/list")

    Call<List<Widget>> widgetList();

  • 指定field

    @FormUrlEncoded

    @POST("user/edit")

    Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last)

  • 指定Http body

    @POST("users/new")

    Call<User> createUser(@Body User user);//必须有相应的Converter

    通用的header能够在okhttp的Interceptors中设定。

使用步骤

1.定义接口

public interface Api {

@GET("tnfs/api/list")

Call<Gallery> getList(@Query(ID) int id, @Query(PAGE) int page, @Query(ROWS) int rows);

}

2.生成对象

public static  Api getApi(){
Retrofit retrofit=new Retrofit.Builder().baseUrl(BASEURL)   //设置域名
            .addConverterFactory(GsonConverterFactory.create())//增加Converter
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//与RxJava整合时须要增加
            .build();
    return  retrofit.create(Api.class);
}

Converter用于将请求结果转化成须要的数据。如GsonConverter将JSON请求结果用Gson解析成Java对象。Retrofit提供的Converter

  • Gson: com.squareup.retrofit2:converter-gson
  • Jackson: com.squareup.retrofit2:converter-jackson
  • Moshi: com.squareup.retrofit2:converter-moshi
  • Protobuf: com.squareup.retrofit2:converter-protobuf
  • Wire: com.squareup.retrofit2:converter-wire
  • Simple XML: com.squareup.retrofit2:converter-simplexml
  • Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars

3.调用接口

异步调用

NetworkApi.getApi().getList(mId,1,8).enqueue(new Callback<Gallery>() {
@Override
public void onResponse(Response<Gallery> response, Retrofit retrofit) {
        mAdapter.setItemList(response.body().getList());
}
 @Override
 public void onFailure(Throwable t) {
     }
});

同步调用

Callery gallery= NetworkApi.getApi().getList(mId,1,8).excute();

Retrofit + RxJava

方法的返回类型定义为Observable

Observable<Gallery> getList(@Query(ID) int id, @Query(PAGE) int page, @Query(ROWS) int rows);

须要增加addCallAdapterFactory(RxJavaCallAdapterFactory.create()才干够识别Observable接口。

NetworkApi.getApi().getList(mId,1,8).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Gallery>() {
    @Override
    public void onCompleted() {}
    @Override
    public void onError(Throwable e) {}
    @Override
    public void onNext(Gallery gallery) {}
});

实现原理

使用OKHTTP运行网络请求

使用动态代理为接口生成实现类。

代理模式介绍

主要作用

1. 方法增强

你能够在不改动源代码的情况下,增强一些方法,在方法运行前后做不论什么你想做的事情。比方。比方能够增加调用日志。做事务控制等。

2. 远程调用

Android中的跨进程通信。

服务端为实现类。client为代理类。实现同样的接口可是代理类并不须要实现接口定义的功能。

而是使用binder机制向服务端发起请求,服务端返回结果给client。

服务端和client还能够跨网络。

动态代理

为某个类自己主动产生代理类。该类必须实现一个接口。

原理

在运行时自己主动产生代码并进行编译,然后用classloader载入字节码生成Class类,用反射调用Class类的构造函数生成对象。

public class CachedProviderHandler implements InvocationHandler {
private Map<String, Object> cached = new HashMap<>();
private Object target;

public CachedProviderHandler(Object target) {
    this.target = target;
}

public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable {
    Type[] types = method.getParameterTypes();
    if (method.getName().matches("get.+") && (types.length == 1) &&
            (types[0] == String.class)) {
        String key = (String) args[0];
        Object value = cached.get(key);
        if (value == null) {
            value = method.invoke(target, args);
            cached.put(key, value);
        }
        return value;
    }
    return method.invoke(target, args);
}
}

public abstract class ProviderFactory {
public static FontProvider getFontProvider() {
    Class<FontProvider> targetClass = FontProvider.class;
    return (FontProvider) Proxy.newProxyInstance(targetClass.getClassLoader(),
        new Class[] { targetClass },
        new CachedProviderHandler(new FontProviderFromDisk()));
}
}

怎样自己主动产生代码

// 假设需代理接口 Simulator
public interface Simulator {
short simulate(int arg1, long arg2, String arg3) throws ExceptionA, ExceptionB;
} 

// 假设代理类为 SimulatorProxy, 其类声明将例如以下
final public class SimulatorProxy implements Simulator { 

// 调用处理器对象的引用
protected InvocationHandler handler; 

// 以调用处理器为參数的构造函数
public SimulatorProxy(InvocationHandler handler){
    this.handler = handler;
} 

// 实现接口方法 simulate
public short simulate(int arg1, long arg2, String arg3)
    throws ExceptionA, ExceptionB {

    // 第一步是获取 simulate 方法的 Method 对象
    java.lang.reflect.Method method = null;
    try{
        method = Simulator.class.getMethod(
            "simulate",
            new Class[] {int.class, long.class, String.class} );
    } catch(Exception e) {
        // 异常处理 1(略)
    } 

    // 第二步是调用 handler 的 invoke 方法分派转发方法调用
    Object r = null;
    try {
        r = handler.invoke(this,
            method,
            // 对于原始类型參数须要进行装箱操作
            new Object[] {new Integer(arg1), new Long(arg2), arg3});
    }catch(Throwable e) {
        // 异常处理 2(略)
    }
    // 第三步是返回结果(返回类型是原始类型则须要进行拆箱操作)
    return ((Short)r).shortValue();
}
}

Volley vs Retrofit

  • Retrofit更好用
  • Volley提供了载入图片的支持
  • Volley有缓存,Retrofit能够借助Okhttp提供缓存
  • Volley因为仅仅有四个线程,缓存默认仅仅有5M不适合大文件处理

參考

http://gank.io/post/560e15be2dca930e00da1083#toc_26

给 Android 开发人员的 RxJava 具体解释

http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html

Java 动态代理机制分析及扩展

http://square.github.io/retrofit/

Retrofit官网

时间: 2024-08-29 02:04:37

Volley 解析的相关文章

Android volley 解析(四)之缓存篇

这是 volley 的第四篇 blog 了,写完这篇,volley 的大部分用法也都算写了一遍,所以暂时不会写 volley 的文章了,如果想看我前面写的文章,可以点这里 Android volley 解析(三)之文件上传篇 为什么要用缓存 我们知道,当客户端在请求网络数据的时候,是需要消耗流量的,特别是对于移动端用户来说,对于流量的控制要求很高.所以在做网络请求的时候,如果对数据更新要求不是特别高,往往都会用到缓存机制,一方面能减少对服务端的请求,控制流量:另一方面,当客户端在没有网络的情况下

Android Volley解析(二)之表单提交篇

上一篇文章中,讲了 Volley 的 get 和 post 请求,并且对 volley 的基本使用和基本分析做了讲解,而这篇 blog 将讲解用 volley 实现表单的提交,在看这篇文章之前,如果对 Volley 基本知识不够了解的朋友,可以移驾前往Android Volley解析(一)之GET.POST请求篇 表单提交的数据格式 要实现表单的提交,就要知道表单提交的数据格式是怎么样,这里我从某知名网站抓了一条数据,先来分析别人提交表单的数据格式. 数据包: Connection: keep-

Android volley 解析(三)之文件上传篇

前面我们讲了如何通过 volley 实现表单的提交,而这篇文章跟上一篇衔接很大,如果没有看上一篇 blog 的朋友,建议先去看看 Android Volley解析(二)之表单提交篇 因为文件上传实质就是表单的提交,只不过它提交的数据包含文件类型,接下来还是按照表单提交的套路来分析. 数据格式 这里我们通过图片上传的案例来分析,其他文件也是同样的实现方式:以下是我在传图网传图时,上传的数据格式,先来分析一下 POST http://chuantu.biz/upload.php HTTP/1.1 H

Android Volley解析(一)之GET、POST请求篇

一. Volley 的地位 自2013年Google I/O 大会上,Google 推出 Volley 之后,一直到至今,由于其使用简单.代码轻量.通信速度快.并发量大等特点,倍受开发者们的青睐. 先看两张图,让图片告诉我们 Volley 的用处: 第一张 Volley 的经典图 通过上图,我们可以发现 Volley适合网络通信频繁操作,并能同时实现多个网络通信. 第二张图 我们在以前在 ListView 的 item 中如果有网络请求,一般都是通过Task 异步任务来完成,并在完成之后通知 A

Android -- Volley解析

Volley设计 Dispatch Thread不断从RequestQueue中取出请求,根据是否已缓存调用Cache或Network这两类数据获取接口之一,从内存缓存或是服务器取得请求的数据,然后交由ResponseDelivery去做结果分发及回调处理. Volley中的类简介 Volley:过 newRequestQueue(…) 函数新建并启动一个请求队列RequestQueue. Request:表示一个请求的抽象类.StringRequest.JsonRequest.ImageReq

volley源码解析(七)--最终目的之Response&lt;T&gt;

在上篇文章中,我们最终通过网络,获取到了HttpResponse对象 HttpResponse是android包里面的一个类,然后为了更高的扩展性,我们在BasicNetwork类里面看到,Volley将其包装成一个Volley自己的对象NetworkResponse 另外,在BasicNetwork类中我们也注意到,对HttpResponse包装成NetworkResponse的过程中,使用HttpResponse的Inputstream,将数据保存在一个byte[]数组中. BasicNet

Android JSON原生解析的几种思路,以号码归属地,笑话大全,天气预报为例演示

Android JSON原生解析的几种思路,以天气预报为例 今天项目中要实现一个天气的预览,加载的信息很多,字段也很多,所以理清了一下思路,准备独立出来写一个总结,这样对大家还是很有帮助的,老司机要开车了 涉及到网络,你就一定要先添加权限,准没错 <!--网络权限--> <uses-permission android:name="android.permission.INTERNET" /> 一.归属地查询(JSONObject) 这个也是最简单的一类Json

volley介绍08

----------------------------------------------------------------------------------- 转载:http://blog.csdn.net/crazy__chen/article/details/46612901 ----------------------------------------------------------------------------------- 在上篇文章中,我们最终通过网络,获取到了H

Android JSON解析库Gson和Fast-json的使用对比和图书列表小案例

Android JSON解析库Gson和Fast-json的使用对比和图书列表小案例 继上篇json解析,我用了原生的json解析,但是在有些情况下我们不得不承认,一些优秀的json解析框架确实十分的好用,今天我们为了博客的保质保量,也就不分开写,我们直接拿比较火的Gson和Fast-json来使用,末尾在进行一些分析 Android JSON原生解析的几种思路,以号码归属地,笑话大全,天气预报为例演示 一.各有千秋 两大解析库的东家都是巨头,一个来自于Google官方,一个来自阿里巴巴,我们这