从源码分析Android的Volley库的工作流程

Volley现在已经被官方放到AOSP里面,已经逐步成为Android官方推荐的网络框架。

类抽象
对Http协议的抽象
Requeset
顾名思义,对请求的封装,实现了Comparable接口,因为在Volley中是可以指定请求的优先级的,实现Comparable是为了在Request任务队列中进行排序,优先级高的Request会被优先调度执行。
NetworkResponse
Http响应的封装,其中包括返回的状态码 头部 数据等。
Response
给调用者返回的结果封装,它比NetworkResponse更加简单,只包含三个东西:数据 异常 和 Cache数据。
Network
对HttpClient的抽象,接受一个Request,返回一个NetworkResponse

反序列化抽象
所谓反序列化,就是将网络中传输的对象变成一个Java对象,Volley中是通过扩展Request类来实现不同的反序列化功能,如JsonRequest StringRequest,我们也可以通过自己扩展一些Request子类,来实现对请求流的各种定制。

请求工作流抽象

RequestQueue
用来管理各种请求队列,其中包含有4个队列
a) 所有请求集合,通过RequestQueue.add()添加的Request都会被添加进来,当请求结束之后删除。
b) 所有等待Request,这是Volley做的一点优化,想象一下,我们同时发出了三个一模一样的Request,此时底层其实不必真正走三个网络请求,而只需要走一个请求即可。所以Request1被add之后会被调度执行,而Request2 和Request3被加进来时,如果Request1还未执行完毕,那么Request2和 Request3只需要等着Request1的结果即可。
c) 缓存队列,其中的Request需要执行查找缓存的工作
d) 网络工作队列 其中的Request需要被执行网络请求的工作

NetworkDispatcher
执行网络Request的线程,它会从网络工作队列中取出一个请求,并执行。Volley默认有四个线程作为执行网络请求的线程。

CacheDispatcher
执行Cache查找的线程,它会从缓存队列中取出一个请求,然后查找该请求的本地缓存。Volley只有一个线程执行Cache任务。

ResponseDelivery
请求数据分发器,可以发布Request执行的结果。

Cache
对Cache的封装,主要定义了如何存储,获取缓存,存取依据Request中的getCacheKey()来描述。

提交请求
Volley通过RequestQueue.add(Request)来往任务队列中增加请求:

一个Request被提交之后有几个去处:

1.Set<Request<?>> mCurrentRequests对应所有请求队列。所有调用add的Request必然都会添加到这里面来。
2.PriorityBlockingQueue<Request<?>> mNetworkQueue 对应网络队列。如果一个Request不需要缓存,那么add之后会被直接添加到网络队列中。
3.PriorityBlockingQueue<Request<?>> mCacheQueue对应缓存请求。如果一个Request需要缓存,并且当前的RequestQueue中并没有一个Request的getCacheKey和当前Request相同(可以认为一个请求),那么加入缓存队列,让缓存工作线程来处理。
4.Map<String, Queue<Request<?>>> mWaitingRequests对应等待队列。如果RequestQueue中已经有一个相同请求在处理,这里只需要将这个Request放到等待队列中,等之前的Request结果回来之后,进行处理即可。

Volley提交任务到队列中是不是很简单?下面来说说优先级请求的事情吧,你可能已经注意到了,上面两个存放需要执行任务的队列都是PriorityBlockingQueue,前面说了Request现实了Comparable,看看这个方法:

@Override
public int compareTo(Request<T> other) {
 Priority left = this.getPriority();
 Priority right = other.getPriority();
  //mSequence表示请求序列号,add时,会通过一个计数器来指定
  return left == right ?
  this.mSequence - other.mSequence :
  right.ordinal() - left.ordinal();
}

所以,如果我们的工作线程(NetworkDispatcher,CacheDispatcher)取任务时,自然会从头部开始取。

这里的优先级,仅仅是保证一个请求比另外一个请求先处理,而并不能保证一个高优先级请求一定会比低优先级的请求先回来

缓存工作线程处理

 @Override
 public void run() {
 //初始化Cache
 mCache.initialize();
 Request<?> request;
 while (true) {
   //阻塞 获取一个Cache任务
   request = mCacheQueue.take();
  try {
   //已经被取消
    if (request.isCanceled()) {
    request.finish("cache-discard-canceled");
    continue;
   }
   //如果拿cache未果,放入网络请求队列
   Cache.Entry entry = mCache.get(request.getCacheKey());
   if (entry == null) {
    request.addMarker("cache-miss");
    mNetworkQueue.put(request);
    continue;
   }
   //缓存超时,放入网络请求队列
   if (entry.isExpired()) {
    request.addMarker("cache-hit-expired");
    request.setCacheEntry(entry);
    mNetworkQueue.put(request);
    continue;
   }
   //根据Cache构造Response
   Response<?> response = request.parseNetworkResponse(
     new NetworkResponse(entry.data, entry.responseHeaders));
   //是否超过软过期
   if (!entry.refreshNeeded()) {
    // 直接返回Cache
    mDelivery.postResponse(request, response);
   } else {
    request.setCacheEntry(entry);
    //设置中间结果
    response.intermediate = true;
    //发送中间结果
    final Request<?> finalRequest = request;
    mDelivery.postResponse(request, response, new Runnable() {
     @Override
     public void run() {
      try {
       //中间结果完事之后,讲请求放入网络队列
       mNetworkQueue.put(finalRequest);
      } catch (InterruptedException e) {
       // Not much we can do about this.
      }
     }
    });
   }
  } catch (Exception e) {

  }
 }
}

这里可以看到Volley确实对缓存封装很到位,各种情况都考虑到了,其中比较重要的两点:

取出来的Cache并不仅仅是数据,同时还包括这次请求的一些Header
硬过期 软过期
我们可以看到Cache中有两个字段来描述缓存过期: Cache.ttl vs Cache.softTtl。什么区别呢?如果ttl过期,那么这个缓存永远不会被使用了;如果softTtl没有过期,这个数据直接返回;如果softTtl过期,那么这次请求将有两次返回,第一次返回这个Cahce,第二次返回网络请求的结果。想想,这个是不是满足我们很多场景呢?先进入页面展示缓存,然后再刷新页面;如果这个缓存太久了,可以等待网络数据回来之后再展示数据,是不是很赞?
NetworkDispatcher
执行网络请求的工作线程,默认有4个线程,它不停地从网络队列中取任务执行。

public void run() {
 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
 Request<?> request;
 while (true) {
  long startTimeMs = SystemClock.elapsedRealtime();
  // release previous request object to avoid leaking request object when mQueue is drained.
  request = null;
  try {
   request = mQueue.take();
  } catch (InterruptedException e) {
   if (mQuit) {
    return;
   }
   continue;
  }

  try {
   request.addMarker("network-queue-take");
   //取消
   if (request.isCanceled()) {
    request.finish("network-discard-cancelled");
    continue;
   }
   //通过Http栈实现客户端发送网络请求
   NetworkResponse networkResponse = mNetwork.performRequest(request);
   request.addMarker("network-http-complete");

   // 如果缓存软过期,那么会重新走网络;如果server返回304,表示上次之后请求结果数据本地并没有过期,所以可以直接用本地的,因为之前Volley已经发过一次Response了,所以这里就不需要再发送Response结果了。
   if (networkResponse.notModified && request.hasHadResponseDelivered()) {
    request.finish("not-modified");
    continue;
   }

   Response<?> response = request.parseNetworkResponse(networkResponse);
   request.addMarker("network-parse-complete");
   //更新缓存
   if (request.shouldCache() && response.cacheEntry != null) {
    mCache.put(request.getCacheKey(), response.cacheEntry);
    request.addMarker("network-cache-written");
   }
   //发送结果
   request.markDelivered();
   mDelivery.postResponse(request, response);
  } catch (VolleyError volleyError) {
   volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
   parseAndDeliverNetworkError(request, volleyError);
  } catch (Exception e) {
   VolleyLog.e(e, "Unhandled exception %s", e.toString());
   VolleyError volleyError = new VolleyError(e);
   volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
   mDelivery.postError(request, volleyError);
  }
 }
}

Request
Request中主要封装了一个请求的各类Http协议信息,比如 URL,请求方法,请求的优先级,请求重试的策略,缓存策略等。

这里说一下其中比较有意思的重发策略,如果一次请求发生超时异常,比如SocketTimeoutException  ConnectTimeoutException ,我们可以为Request配置一个RetryPolicy,你可以指定重发这个Request的次数,以及每次失败之后重新设置这个请求的超时时间(第一次失败之后,你可以调整第二次请求的超时时间增加,以减少失败的可能性)。

反序列化
Request最重要的功能就是提供了内容的反序列化,通过不同的子类来实现不同的序列化功能。比如,如果请求结果是一个Json的对象,我们可以使用JsonObjectRequest,如果是一个普通字符,使用StringRequest,同时,我们也可以很方便的定制自己的Request,通过复写Response<T> parseNetworkResponse(NetworkResponse response);方法即可。

默认的JsonRequest使用org.json中的Json解析,我们使用Gson来进行解析能够构造一个更加通用的处理json返回的Request:

 public class JsonGRequest<T> extends Request<T> {

private static Gson gson = new Gson();

private Response.Listener<T> mListener;

public JsonGRequest(String url, Response.ErrorListener listener,Response.Listener responseListener) {
 super(url, listener);
 this.mListener = mListener;
}

public JsonGRequest(int method, String url, Response.ErrorListener listener) {
 super(method, url, listener);
}

@Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
 return Response.success(gson.fromJson(new InputStreamReader(new ByteArrayInputStream(response.data)),getType()), HttpHeaderParser.parseCacheHeaders(response))
}

@Override
protected void deliverResponse(T response) {
 if(mListener != null) {
  mListener.onResponse(response);
 }
}

//获取指定的泛型类型
 protected Type getType() {
 Type superclass;
 for(superclass = this.getClass().getGenericSuperclass(); superclass instanceof Class && !superclass.equals(JsonGRequest.class); superclass = ((Class)superclass).getGenericSuperclass()) {
  ;
 }

 if(superclass instanceof Class) {
  throw new RuntimeException("Missing type parameter.");
 } else {
  ParameterizedType parameterized = (ParameterizedType)superclass;
  return parameterized.getActualTypeArguments()[0];
 }
}
}

ImageRequest
Volley专门为图片请求提供了ImageRequest,主要是反序列化了一下数据流到BitMap,还可以制定图片的大小,质量等参数。

ImageLoader是Volley提供的一个用来加载图片的工具,它的内部还是使用ImageRequest来实现的,主要新加的功能是增加了内存缓存,你可以通过配置ImageCache来设置内存缓存。

(0)

相关推荐

  • Android Volley框架使用源码分享

    过去在Android上网络通信都是使用的Xutils 因为用它可以顺道处理了图片和网络这两个方面,后来发觉Xutils里面使用的是HttpClient  而Google在6.0的版本上已经把HttpClient废除了,所以开始寻找新的网络框架,okhttp也用过,但是它是在作用在UI线程,使用起来还需要用handler 所以就先用着Volley框架了.  这里我先分析下Volley框架的简单网络请求的源码. 使用Volley请求网络数据的简单过程: RequestQueue queue = Vo

  • Android的HTTP操作库Volley的基本使用教程

    以前原本都用android内建的Library来进行GET.POST等等对API的连线与操作. 但最近想说来找找看有没有好用的library,应该可以事半功倍. 当初有找了三套比较多人用的 1.Android Asynchronous Http Client 2.okhttp square开发并且开源的,因为之前用过他们家的picasso,所以对这套满有好感的,只可惜使用方式不太喜欢 3.Volley Volley是Google在2013年Google I/O的时候发布的,到现在已经积累了很高的

  • Android 网络请求框架Volley实例详解

    Android 网络请求框架Volley实例详解 首先上效果图 Logcat日志信息on Reponse Volley特别适合数据量不大但是通信频繁的场景,像文件上传下载不适合! 首先第一步 用到的RequetQueue RequestQueue.Java RequestQueue请求队列首先得先说一下,ReuqestQueue是如何对请求进行管理的...RequestQueue是对所有的请求进行保存...然后通过自身的start()方法开启一个CacheDispatcher线程用于缓存调度,开

  • 深入剖析Android的Volley库中的图片加载功能

    一.基本使用要点回顾 Volley框架在请求网络图片方面也做了很多工作,提供了好几种方法.本文介绍使用ImageLoader来进行网络图片的加载. ImageLoader的内部使用ImageRequest来实现,它的构造器可以传入一个ImageCache缓存形参,实现了图片缓存的功能,同时还可以过滤重复链接,避免重复发送请求. 下面是ImageLoader加载图片的实现方法: public void displayImg(View view){ ImageView imageView = (Im

  • Android的HTTP类库Volley入门学习教程

    1. 什么是Volley 我们平时在开发Android应用的时候不可避免地都需要用到网络技术,而多数情况下应用程序都会使用HTTP协议来发送和接收网络数据.Android系统中主要提供了两种方式来进行HTTP通信,HttpURLConnection和HttpClient,几乎在任何项目的代码中我们都能看到这两个类的身影,使用率非常高. 不过HttpURLConnection和HttpClient的用法还是稍微有些复杂的,如果不进行适当封装的话,很容易就会写出不少重复代码.于是乎,一些Androi

  • Android开发中使用Volley库发送HTTP请求的实例教程

    Android Volley 是Google开发的一个网络lib,可以让你更加简单并且快速的访问网络数据.Volley库的网络请求都是异步的,你不必担心异步处理问题. Volley的优点: 请求队列和请求优先级 请求Cache和内存管理 扩展性性强 可以取消请求 下载和编译volley.jar 需要安装git,ant,android sdk clone代码: git clone https://android.googlesource.com/platform/frameworks/volley

  • Android Volley框架全面解析

     Volley简介 我们平时在开发Android应用的时候不可避免地都需要用到网络技术,而多数情况下应用程序都会使用HTTP协议来发送和接收网络数据.Android系统中主要提供了两种方式来进行HTTP通信,HttpURLConnection和HttpClient,几乎在任何项目的代码中我们都能看到这两个类的身影,使用率非常高. 不过HttpURLConnection和HttpClient的用法还是稍微有些复杂的,如果不进行适当封装的话,很容易就会写出不少重复代码.于是乎,一些Android网络

  • Android 开发中Volley详解及实例

    Android 开发中Volley详解及实例 最近在做项目的时候,各种get和post.简直要疯了,我这种啥都不了解的,不知道咋办了,然后百度看了下,可以用volley进行网络请求与获取,下面就介绍下volley的用法. volley有三种方式:JsonObjectRequest,JsonArrayRequest,StringRequest.其实都是差不多了,举一反三就ok了,这里我就讲下JsonObjectRequest. 方法如下: JsonObjectRequest jsonObjectR

  • 从源码分析Android的Volley库的工作流程

    Volley现在已经被官方放到AOSP里面,已经逐步成为Android官方推荐的网络框架. 类抽象 对Http协议的抽象 Requeset 顾名思义,对请求的封装,实现了Comparable接口,因为在Volley中是可以指定请求的优先级的,实现Comparable是为了在Request任务队列中进行排序,优先级高的Request会被优先调度执行. NetworkResponse Http响应的封装,其中包括返回的状态码 头部 数据等. Response 给调用者返回的结果封装,它比Networ

  • 从源码分析Android的Glide库的图片加载流程及特点

    0.基础知识 Glide中有一部分单词,我不知道用什么中文可以确切的表达出含义,用英文单词可能在行文中更加合适,还有一些词在Glide中有特别的含义,我理解的可能也不深入,这里先记录一下. (1)View: 一般情况下,指Android中的View及其子类控件(包括自定义的),尤其指ImageView.这些控件可在上面绘制Drawable (2)Target: Glide中重要的概念,目标.它即可以指封装了一个View的Target(ViewTarget),也可以不包含View(SimpleTa

  • 源码分析Android rinflate的使用

    目录 rinflate源码解析 递归 类型判断 TAG_REQUEST_FOCUS TAG_TAG TAG_MERGE TAG_INCLUDE parseInclude 第一部分:查找layout属性 第二部分:加载对应的View并替换 总结 这里接上一篇LayoutInflater源码分析继续分析. rinflate源码解析 这里详细理一理rinflate方法,作用就是找到传入的XmlPullParser当前层级所有的view并add到parent上: final void rInflateC

  • Android开发Retrofit源码分析

    目录 项目结构 retrofit 使用 Retrofit #create ServiceMethod #parseAnnotations HttpServiceMethod#parseAnnotations 第二种 非Kotlin协程情况 DefaultCallAdapterFactory#get 第一种 Kotlin协程情况 总结 项目结构 把源码 clone 下来 , 可以看到 retrofit 整体结构如下 图 http包目录下就是一些http协议常用接口 , 比如 请求方法 url ,

  • Android透明化和沉浸式状态栏实践及源码分析

    本文所提到的透明状态栏其实指的是将顶部的导航栏延伸到状态栏,使之浑然一体(Google官方建议状态栏颜色比导航栏的颜色略深一点),并不代表一定不设置背景色,比如导航栏是白色,则可设置状态栏为白色,视情况而定. 相比于iOS系统,Android系统对于状态栏的设置就显得稍微复杂了一点.Android系统提供了API 19以上对状态栏的设置接口,而直到API 23以上才提供对于icon颜色的设置,还有就是各家厂商(如魅族,小米等)对于状态栏的有自己的定制,对于需要使用浅色背景状态栏的应用,没处理好的

  • 从源码编译Android系统的Java类库和JNI动态库的方法

    利用源码编译Android系统Java类库 1.编写Java项目和Android.mk文件 ├── Android.mk └── src └── com └── lhw └── framework └── led └── Led.java Led.java文件 package com.lhw.framework.led; /** * LED操作库 * @author Micky Liu */ public class Led { public boolean turnOn() { return

  • Android AsyncTask源码分析

    Android中只能在主线程中进行UI操作,如果是其它子线程,需要借助异步消息处理机制Handler.除此之外,还有个非常方便的AsyncTask类,这个类内部封装了Handler和线程池.本文先简要介绍AsyncTask的用法,然后分析具体实现. 基本用法 AsyncTask是一个抽象类,我们需要创建子类去继承它,并且重写一些方法.AsyncTask接受三个泛型参数: Params: 指定传给任务执行时的参数的类型 Progress: 指定后台任务执行时将任务进度返回给UI线程的参数类型 Re

  • Android getJSONObject与optJSONObject的区别结合源码分析

    Android getJSONObject与optJSONObject的区别结合源码分析 json解析常见问题: getJSONObject与optJSONObject的区别,下面结合源码和案例来分析当我们使用这两周方法来解析数据时,哪种比较好. 源码分析: //使用getJSONObject时,如果返回的对象不是JSONObject,抛出JSONException异常 /** * Returns the value mapped by {@code name} if it exists and

  • Android实例HandlerThread源码分析

    HandlerThread 简介: 我们知道Thread线程是一次性消费品,当Thread线程执行完一个耗时的任务之后,线程就会被自动销毁了.如果此时我又有一 个耗时任务需要执行,我们不得不重新创建线程去执行该耗时任务.然而,这样就存在一个性能问题:多次创建和销毁线程是很耗 系统资源的.为了解这种问题,我们可以自己构建一个循环线程Looper Thread,当有耗时任务投放到该循环线程中时,线程执行耗 时任务,执行完之后循环线程处于等待状态,直到下一个新的耗时任务被投放进来.这样一来就避免了多次

随机推荐