DevSupportAndroid原理鱼乐

手撕 Volley (一)

2016-10-04  本文已影响17087人  SavySoda

android_volley_tutorial.jpg

前言

从去年开始使用Volley,到现在一年多了。前几天参加某互联网公司校招被问到Volley相对其他的网络框架有什么优缺点,它分别是如何实现的。当时答得的并不好。所以趁十一假期读一下Volley的源码。

写这篇文章的目的有两个:1. 总结下 Android 网络编程,学习 Volley 设计思想。2. 给正在使用 Volley 但仍然心存疑惑的人一些更深入的解析。


Volley到底是什么

Volley Github主页
Android 网络通信框架Volley简介(Google IO 2013)

Volley简介

volley 是 Goole I/O 2013上发布的网络通信库,使网络通信更快、更简单、更健壮。
关键词:数据不大但通信频繁
Volley名称的由来: a burst or emission of many things or a large amount at once

Volley提供的功能

Volley好在哪

HttpClient、HttpURLConnection、OKHttp和Volley优缺点和性能对比

物理质量
Volley 的优点
Volley 的缺点

Volley 的使用场景和使用方式

关于 Volley 怎么用网络上的文章太多了,链接整理如下

Android Volley完全解析(一),初识Volley的基本用法
Android Volley完全解析(二),使用Volley加载网络图片
Android Volley完全解析(三),定制自己的Request
Android Volley 之自定义Request
官方教程(需要翻墙)
An Introduction to Volley


Http 权威指南笔记

不是要读 Volley 的源码嘛,怎么又看起了 HTTP 权威指南,原因很简单,Volley 源码里面有很多处理是与 HTTP 协议息息相关的,只有了解了协议才能更深入的理解Volley。Volley里面涉及协议的地方都会在注释中给出协议文档的链接。

Hypertext Transfer Protocol -- HTTP/1.1
HTTP权威指南读书笔记
HTTP协议详解(真的很经典)
HTTP协议详解

这里简单介绍一几个概念:


HTTP.png

下面给出一组请求和响应的样例。


HTTP_RequestMessageExample.png
HTTP_ResponseMessageExample.png

HttpURLConnection 与 HttpClient

这里了解一下 httpClient 和 HttpURLConnection 的区别和历史,并主要学习一下 HttpURLConnection 的使用,android 社区现在更推荐使用 HttpURLConnection 来进行网络开发。
需要注意的是 android 6.0 SDK,不再提供 org.apache.http 的支持,所以 6.0 以后要想使用 Volley(HttpClient)需要手动配置 gradle 了。

HttpURLConnection(官方文档,需要翻墙)
Interface HttpClient
A Comparison of java.net.URLConnection and HTTPClient
HttpClient和HttpURLConnection的区别


Volley 源码解析

ok 做完了前面的准备工作终于可以开始最激动人心的部分了,首先来一张官方给出的流程图。

volley-request.png

对于这张图我们只需要知道:

再来一张总体设计图:


flow.png
入口

我用的 Sublime Text 3 来阅读 Volley ,可以看到 Volley 中大大小小一共43个类。
我们使用 Volley 的第一步是通过Volley 的 newRequestQueue 方法得到 一个RequestQueue 队列。那么我们就从这个方法开始吧。不管几个参数的 newRequestQueue 方法最终都会调用下面这个三个参数的。

QQ截图20161004202730.png

可以看到:

  1. 在磁盘上创建一块文件
  2. 设置 UserAgent ,不知道什么是UserAgent去看前面的HTTP协议
  3. 根据 SDK 版本的不同初始化 HTTPStack
  4. 用 HTTPStack 初始化 BasicNetwork
  5. 用第一步创建的文件初始化磁盘缓存
  6. 用磁盘缓存和 NetWork 创建我们的 请求队列 RequestQueue
  7. 调用 RequestQueue 的 start 方法

这里面我们有几个疑问

首先看第一个 HttpStack

/**
 * An HTTP stack abstraction.
 */
public interface HttpStack {
    /**
     * Performs an HTTP request with the given parameters.
      */
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
        throws IOException, AuthFailureError;

}
/**
 * An HttpStack that performs request over an {@link HttpClient}.
 */
public class HttpClientStack implements HttpStack {
/**
 * An {@link HttpStack} based on {@link HttpURLConnection}.
 */
public class HurlStack implements HttpStack {

HttpStack 类图


HttpStack.png

很明显 HttpStack 是一个接口并且只有一个 performRequest 方法。
而 HurlStack 和 HttpClientStack 分别是基于 HttpUrlConnection 和 HttpClient 对 HttpStack 的实现,是真正用来访问网络的类。内部具体实现后面再看。
下面再来看NetWork 和 BasicNetWork:

/**
 * An interface for performing requests.
 */
public interface Network {
    /**
     * Performs the specified request.
     * @param request Request to process
     * @return A {@link NetworkResponse} with data and caching metadata; will never be null
     * @throws VolleyError on errors
     */
    public NetworkResponse performRequest(Request<?> request) throws VolleyError;
}


}
/**
 * A network performing Volley requests over an {@link HttpStack}.
 */
public class BasicNetwork implements Network {
    protected static final boolean DEBUG = VolleyLog.DEBUG;

    private static int SLOW_REQUEST_THRESHOLD_MS = 3000;

    private static int DEFAULT_POOL_SIZE = 4096;

    protected final HttpStack mHttpStack;

    protected final ByteArrayPool mPool;

    /**
     * @param httpStack HTTP stack to be used
     */
    public BasicNetwork(HttpStack httpStack) {
        // If a pool isn't passed in, then build a small default pool that will give us a lot of
        // benefit and not use too much memory.
        this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
    }

    /**
     * @param httpStack HTTP stack to be used
     * @param pool a buffer pool that improves GC performance in copy operations
     */
    public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) {
        mHttpStack = httpStack;
        mPool = pool;
    }



}
Network.png

可以看到同样是接口与实现类的关系,内部封装了一个 HttpStack 用来是想网络请求。
接口的方法是一样的,这又是为什么呢,这里暂且不管,后面看实现再分析。
接下来是 DiskBasedCache:

/**
 * Cache implementation that caches files directly onto the hard disk in the specified
 * directory. The default disk usage size is 5MB, but is configurable.
 */
public class DiskBasedCache implements Cache {

可以看得到 DiskBaseCache 继承自 Cache接口,老规矩我们先看接口不看具体实现,先知道他是干嘛的。


/**
 * An interface for a cache keyed by a String with a byte array as data.
 */
public interface Cache {
    /**
     *  an entry from the cache.
     * @param key Cache key
     * @return An {@link Entry} or null in the event of a cache miss
     */
    public Entry get(String key);
    public void put(String key, Entry entry);
    public void initialize();  
    public void invalidate(String key, boolean fullExpire);
    public void remove(String key);
    public void clear();

    /**
     * Data and metadata for an entry returned by the cache.
     */
    public static class Entry {
        /** The data returned from cache. */
        public byte[] data;

        /** ETag for cache coherency. */
        public String etag;

        /** Date of this response as reported by the server. */
        public long serverDate;

        /** The last modified date for the requested object. */
        public long lastModified;

        /** TTL for this record. */
        public long ttl;

        /** Soft TTL for this record. */
        public long softTtl;

        /** Immutable response headers as received from server; must be non-null. */
        public Map<String, String> responseHeaders = Collections.emptyMap();

        /** True if the entry is expired. */
        public boolean isExpired() {
            return this.ttl < System.currentTimeMillis();
        }

        /** True if a refresh is needed from the original data source. */
        public boolean refreshNeeded() {
            return this.softTtl < System.currentTimeMillis();
        }
    }

}

Cache.png

又是接口,Volley 核心功能的实现都是基于接口的。
我们来看,接口 Cache 里面封装了一个静态内部类 Entry(登记),这个内部类非常重要,看了 HTTP 协议的同学们会发现,Entry 里面定义的这些成员变量跟 headers(消息报头)里面关于缓存的标签是一样的,这也是前面强调要看协议的原因。其中还维护了一个map 用来保存消息报头中的 key / value,data 来保存 entity 消息实体。除此之外就是一些集合操作了。
我们使用 Volley 的时候创建一个 request 然后把它丢到 RequestQueue 中就可以了。那么来看 RequestQueue 的构造方法,下面是最终会调用的构造器。

public RequestQueue(Cache cache, Network network, int threadPoolSize,
            ResponseDelivery delivery) {
        mCache = cache;
        mNetwork = network;
        mDispatchers = new NetworkDispatcher[threadPoolSize];
        mDelivery = delivery;
    }

设置了几个成员变量,那么 RequestQueue到底有哪些成员变量呢

/** Used for generating monotonically-increasing sequence numbers for requests. */
    private AtomicInteger mSequenceGenerator = new AtomicInteger();

    /**
     * Staging area for requests that already have a duplicate request in flight.
     *
     * <ul>
     *     <li>containsKey(cacheKey) indicates that there is a request in flight for the given cache
     *          key.</li>
     *     <li>get(cacheKey) returns waiting requests for the given cache key. The in flight request
     *          is <em>not</em> contained in that list. Is null if no requests are staged.</li>
     * </ul>
     */
    private final Map<String, Queue<Request<?>>> mWaitingRequests =
            new HashMap<String, Queue<Request<?>>>();

    /**
     * The set of all requests currently being processed by this RequestQueue. A Request
     * will be in this set if it is waiting in any queue or currently being processed by
     * any dispatcher.
     */
    private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();

    /** The cache triage queue. */
    private final PriorityBlockingQueue<Request<?>> mCacheQueue =
        new PriorityBlockingQueue<Request<?>>();

    /** The queue of requests that are actually going out to the network. */
    private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
        new PriorityBlockingQueue<Request<?>>();

    /** Number of network request dispatcher threads to start. */
    private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;

    /** Cache interface for retrieving and storing responses. */
    private final Cache mCache;

    /** Network interface for performing requests. */
    private final Network mNetwork;

    /** Response delivery mechanism. */
    private final ResponseDelivery mDelivery;

    /** The network dispatchers. */
    private NetworkDispatcher[] mDispatchers;

    /** The cache dispatcher. */
    private CacheDispatcher mCacheDispatcher;

    private List<RequestFinishedListener> mFinishedListeners =
            new ArrayList<RequestFinishedListener>();

所有的成员变量以及核心方法类图如下,为了直观方法没有加参数:


RequestQueue.png
 /**
     * Starts the dispatchers in this queue.
     */
    public void start() {
        stop();  // Make sure any currently running dispatchers are stopped.
        // Create the cache dispatcher and start it.
        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
        mCacheDispatcher.start();

        // Create network dispatchers (and corresponding threads) up to the pool size.
        for (int i = 0; i < mDispatchers.length; i++) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                    mCache, mDelivery);
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }
    }

先调用了 stop,然后分别调用了dispatcher 的 start

  /**
     * Stops the cache and network dispatchers.
     */
    public void stop() {
        if (mCacheDispatcher != null) {
            mCacheDispatcher.quit();
        }
        for (int i = 0; i < mDispatchers.length; i++) {
            if (mDispatchers[i] != null) {
                mDispatchers[i].quit();
            }
        }
    }

stop 调用了分别调用了 dispatcher 的quit
那么疑问来了,dispatcher 的 quit 和 start 是干嘛呢

 public class CacheDispatcher extends Thread {
 public class NetworkDispatcher extends Thread {

CacheDispatcher 和 NetworkDispatcher 都继承自Thread,start 方法自然是开启一个新的线程那quit,一定是关闭线程了,看一下 Volley 是怎么实现的

  public void quit() {
        mQuit = true;
        interrupt();
    }
   @Override
    public void run() {
        while (true) {
                if (mQuit) {
                    return;
                }
        }
    }

我们忽略具体实现可以看到,run 方法里面是一个 while true 的无限循环,然后用以个标记字段,来控制循环退出。
所以 start 方法做的的事情就很清楚了,先 stop 掉跑着的线程,然后开启一个缓存线程, 一组(默认四个)网络线程,每个里面都有一个while ture 死循环。等待 request add 到 Requestqueue 中,接下来我们就来看五个主要方法中的 add
手撕 Volley(二)

上一篇下一篇

猜你喜欢

热点阅读